转载自: https://mp.weixin.qq.com/s/an373dQGF68zRhXb4qTAXg
运维中有个词叫根故障定位,实际生产中很多异常的根故障最后都会定位到网络这一层。下面以Broken Pipe为例,简述一下如何进行网络问题的定位。
1. 定位思路
第一眼看异常栈直译过来就是管道破裂,为什么会出现管道破裂呢?这篇文章就由此而来,背后牵涉的是我们常常挂在嘴边,面试也经常被问的http 和 tcp 协议的知识,这里埋个伏笔,后面我们由浅入深,慢慢把这个问题解决,同时了解如何运用网络知识解决实际网络问题。
2. 开始抓包
遇到网络问题,自然而然会想到使用tcpdump进行抓包,看看传输过程到底发生了什么。抓包命令如下:
tcpdump tcp port 20004 and host **.**.com -w brokenpipe.cap
这条命令监听20004
端口,把所有的主机和域名都抓下来,保存到brokenpipe.cap
文件中。
常用的tcpdump命令
tcpdump tcp port 端口号 and host 域名 -w 保存文件
3. 在Wireshark数据包中看三次握手
如果没有用过Wireshark 做过网络包分析的,担心直接上来就看包分析有点费劲,下面会对wireshark 先做个简单的介绍。
3.1 Wireshark 简介
首先,把抓下来的包文件存在本地, 打开wireshark 导入网络包,开始有趣的网络漫游之旅。
wireshark 是一款非常流行的网络包分析工具,经常是网络工程师/后端工程师用来分析网络包,解决网络问题的利器。先放一张图出来闻闻味:
截了一张自己本机装的 wireshark 软件,分为四个部分:
- 顶部是菜单栏
- 浅绿色背景是过滤器,这个对于分析包很重要,后面会讲
- 列表那一栏是包列表信息
- 最下面的这个是包详细信息,包含各层协议的内容(应用层、传输层、网络层、数据链路层、物理层)
[图片上传失败...(image-1cde73-1591322222101)]
3.2 Wireshark 过滤器
过滤器单独拿出来说下,因为确实很有用,后面异常分析会用到。
你导入的包可能内容很多,需要使用过滤器筛选一下,过滤器很多种过滤的规则,我列举一下常用的几种:
- 协议过滤
如果抓的包有很多种协议类型,可以输入 tcp 回车只看tcp 协议的包
- IP 过滤
例如 ip.src == 192.168.1.23 (过滤发起地址ip)ip.dst == 12.8.0.1(过滤目标地址ip) ip == 12.0.0.1(过滤源或目的地址)
- 端口过滤
tcp.port == 4980 , 还可以 tcp.port == 4542 or tcp.port == 4528 加入表达式 and、or、in 等等
例如:tcp.port in {80 443 8080}
- HTTP 模式过滤
http.request.method == "GET" 或者 http.request.method == "POST"
- 报文内容过滤
tcp.segment_data contains "202005190001" 过滤tcp 报文内容包括 202005190001 的报文
- 更多wireshark 过滤器可以参考:wireshark-filter
wireshark 的详细使用教程不是今天的重点,就介绍这二部分,后面分析数据包时会穿插着讲,觉得大家有兴趣可以自己抽空玩一玩这个软件。
3.3 三次握手初探
写的三次握手初探 这部分如果看不懂没关系,这里是为了介绍Wireshark写的三次握手,后面会详细解释,详细到直接从网络协议分层开始讲起,如果你这看不懂可以Diss 。 [图片上传失败...(image-85572e-1591322222101)]
如果第一次看Wireshark 网络包,会一脸懵逼,看多了就会越看越喜欢。重点看框出来的,前三行就是三次握手的过程:
- 上图第一行,客户端向服务端发送SYN 数据包,数据长度len 为0,Seq(随机生成包序列号)为2421858999;
- 上图第二行,服务端向客户端回应ACK 数据包,并且发送SYN 数据包,合并一起就是SYN + ACK 数据包,数据长度len 为0,Seq(随机生成包序列号)为1988635269,ack为2421859000 = 第一次握手Seq(2421858999)+1;
- 上图第三行,客户端回应客户端的SYN 数据包,发送ACK 确认数据包,Seq 为 二次握手的ack(2421859000),ack为 1988635270= 二次握手的seq(1988635269)+1;
4. 回顾网络协议分层、三次握手、四次挥手等网络基础知识
4.1 网络协议分层
在解决文章开头的异常,分析数据包之前,我们需要一些预备知识,需要一丢丢基础的网络知识。
首先在直接看Wireshark 的包信息之前,需要来回顾一下计算机网络的知识,大家知道目前主流使用的TCP/IP 五层协议,而不是国际标准化组织(ISO)出的OSI(Open System Interconnection)七层协议。TCP/IP协议栈如下图所示: [图片上传失败...(image-b176c-1591322222101)]
我们可以看到Wireshark 包详情就是TCP/IP 五层的信息,对比上面的图从下往上看(取每个英文单词首字母就是协议简称,例如 HTTP:Hypertext Transfer Protocol ),如下:
[图片上传失败...(image-994d49-1591322222101)]
后面我们看 Wireshark 数据报文时,主要看TCP 所在的传输层报文。
4.2 三次握手
首先我们先看下TCP 报文的报文格式: [图片上传失败...(image-cea1fa-1591322222101)]
下面把TCP 报文的各个部分做了详细说明,分析网络问题不用全看,把加重的部分关注一下就可以了。好学的玩家可以把所有的都看了,不用记,有个概念就可以了。
- 源端口号和目的端口号:各占2个字节(16位),分别写入源端口和目的端口;
- 序号:4字节(32位),TCP连接中字节流每个字节都按顺序编号,这个序号用于标识这个报文段。例如:一段报文序号seq 是201,而报文数据长度为100,下一个报文段的数据序号应该为301(201+100)。
- 确认号 :4字节(32位),期望收到对方下一个报文的序号。这个确认号是和序号seq 有点关系的,不要和ACK(状态标志位)混淆了。
- 首部长度:4位,表示报文数据距离报文起始位置的长度。保留:保留今后可以会用到。
- 数据报状态标志位(非常重要),分为以下6种,二进制1 位表示一种(1代表开启 0 关闭)
- URG:URG=1 代表报文有紧急数据
- ACK:ACK = 1,确认位,TCP中连接建立后,所有报文的ACK 位置都为1;
- PSH: 发送端和接收端都有缓冲区(发送端:写缓冲区 接收端:读缓冲区) 对于发送端:带PSH=1,报文会立即从缓冲区报文推送给服务端 对于服务端:服务端立即将读缓冲区内容推给进程。
- RST:RST=1,代表连接出现严重错误,TCP连接的一方将连接重置了,必须释放连接,重新建立连接;
- SYN:同步SYN,在连接建立时用来同步序号。三次握手时会用到,当SYN=1,ACK =0,表明是发起方请求建立连接,服务方同意建立连接,响应报文SYN=1,ACK =1,前者表明同步连接,后者是确认报文。
- FIN:用来释放连接。当FIN =1,表明此报文的发送方的数据已经发送完毕,并且要求释放。
- 窗口:占2字节,通常用于告知对方自己的能够接受的数据量大小。窗口本质就是一个缓冲区buffer,该字段的值用于告知对方自己剩余的可用缓冲区大小。
- 校验和:奇偶校验,此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。由发送端计算和存储,并由接收端进行验证。
- 紧急指针:只有当 URG 标志置 1 时紧急指针才有效。紧急指针是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。
- 选项:可选的。最常见的可选字段是最长报文大小,又称MSS(Maxinum Segment Size), 每个连接方通常在通信的第一个报文段(连接建立的SYN标志位为1的数据报文)设置这个选项,表示本端能接受的最大报文段的长度。因为长度不一定是32的整数倍,因此要加额外的0作为填充。
- 数据部分:可选的。连接建立和终止时,报文段只有TCP首部。
我们先回顾一下以前计算机网络课堂上学过的TCP传输的三次握手流程: [图片上传失败...(image-aac3c1-1591322222101)]
[图片上传失败...(image-60f60b-1591322222101)]
三次握手的具体过程如下:
-
服务端(接收方)进程启动,准备接收客户端进程的连接请求,此时接收方进入LISTEN(监听)模式;
-
三次握手第一步:客户端向服务端发出连接请求报文,这时报文首部SYN 标志位为1,同时设置一个初始序列号seq = x(随机数); 做完这步动作,发送方进入SYN_SENT (同步已发送状态) 。
名称解释:SYN:同步标志位 seq:包序列编号(每个包都有一个序列号) 第一次握手客户端发送的报文称为同步请求报文,希望与服务端建立同步连接,SYN报文不携带数据。
-
三次握手第二步:服务端收到来自客户端的连接请求报文后,需要确认收货,响应报文中ACK(确认标志位)设置为1,将确认号ack 设置为第一步的请求序列号seq 加1(ack =x+1),另外自己也回客户端一个SYN包(可以建立同步连接),即SYN + ACK包,包序列号seq = y,服务端进入SYN_RCVD(同步收到)状态。
名词解释:ACK:确认状态位(这里ACK=1),这个一定和ack(32位确认序号,这里ack=x+1)区分开,可以看下面的TCP 报文结构体图,ACK是包的状态标志,ack是确认序号。
-
三次握手第三步:客户端收到来自服务端的 SYN + ACK 包,会发送一个ACK 确认包,ACK =1,seq = x+1( 第二步的ack),ack = y+1(第二步的seq+1)。
4.3 四次挥手
四次挥手的状态图如下所示: [图片上传失败...(image-1e7837-1591322222101)]
四次挥手wireshark 包信息如下,可以对照着上图看 [图片上传失败...(image-a50345-1591322222101)]
四次挥手的具体过程如下:
客户端发送FIN 释放连接报文,表示结束连接,报文seq = u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN_WAIT1(终止等待1)状态。
服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE_WAIT(关闭等待)状态。TCP接收方通知上层的应用进程,客户端向服务器方向的发送通道关闭了,这时候处于半关闭状态,即客户端已经没有数据要发送了(已经发了FIN结束信号),但是服务器若发送数据,客户端依然要接受。这个状态要持续一段时间,也就是整个CLOSE_WAIT状态持续的时间。
客户端收到服务器的确认请求后,此时,客户端就进入FIN_WAIT2(终止等待2)状态,等待服务器发送连接释放报文(在服务端Close_Wiat期间还可以接受服务器发送的最后的数据)。
服务端发送完最后的数据,向客户端发送FIN 连接释放报文,ACK =1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,ack 和回复ACK报文一致,ack = u+1, 此时,服务器就进入了LAST_ACK(最后确认)状态,等待客户端的确认。
客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME_WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2 个MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。
网友评论