实战!一次网络问题排查,java关于异常的面试题

安琪拉写的三次握手初探 这部分如果看不懂没关系,这里是为了介绍Wireshark写的三次握手,后面会详细解释,详细到直接从网络协议分层开始讲起,如果你这看不懂可以Diss 安琪拉。

image.png

三次握手

如果第一次看Wireshark 网络包,会一脸懵逼,看多了就会越看越喜欢。重点看框出来的,前三行就是三次握手的过程:

  1. 上图第一行,客户端向服务端发送SYN 数据包,数据长度len 为0,Seq(随机生成包序列号)为2421858999;

  2. 上图第二行,服务端向客户端回应ACK 数据包,并且发送SYN 数据包,合并一起就是SYN + ACK 数据包,数据长度len 为0,Seq(随机生成包序列号)为1988635269,ack为2421859000 = 第一次握手Seq(2421858999)+1;

  3. 上图第三行,客户端回应客户端的SYN 数据包,发送ACK 确认数据包,Seq 为 二次握手的ack(2421859000),ack为 1988635270= 二次握手的seq(1988635269)+1;

公众号【安琪拉的博客】后面会更新一个网络系列:列举常见的网络问题,解决的思路,wireshark分析包方法等。

3. 回顾网络协议分层、三次握手、四次挥手等网络基础知识

================================================================================================

3.1 网络协议分层

=============================================================================

在解决文章开头的异常,分析数据包之前,我们需要一些预备知识,需要一丢丢基础的网络知识。

首先在直接看Wireshark 的包信息之前,需要来回顾一下计算机网络的知识,大家知道目前主流使用的TCP/IP 五层协议,而不是国际标准化组织(ISO)出的OSI(Open System Interconnection)七层协议。TCP/IP协议栈如下图所示:

image.png

TCP/IP五层协议

我们可以看到Wireshark 包详情就是TCP/IP 五层的信息,对比上面的图从下往上看(取每个英文单词首字母就是协议简称,例如 HTTP:Hypertext Transfer Protocol ),如下:

image.png

image-20200515005055601

后面我们看 Wireshark 数据报文时,主要看TCP 所在的传输层报文。

3.2 三次握手

===========================================================================

首先我们先看下TCP 报文的报文格式:

image.png

TCP报文格式

下面把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.png

TCP连接三次握手

image.png

三次握手

三次握手的具体过程如下:

服务端进程启动,准备接收客户端进程的连接请求,此时接收方进入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)。

玩家们如果觉得看安琪拉写的有收获,欢迎关注公众号【安琪拉的博客】,来找安琪拉草丛互动!

3.3 四次挥手

===========================================================================

四次挥手的状态图如下所示:

image.png

四次挥手

四次挥手wireshark 包信息如下,可以对照着上图看,

image.png

Wireshark四次挥手

四次挥手的具体过程如下:

客户端发送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连接。

4.异常定位:Wireshark 分析定位Broken Pipe 异常原因

========================================================================================================

4.1 网络包分析

============================================================================

上面我们已经看了正常的四次挥手的流程和截图,下面我们来看下线上遇到的 Broken Pipe异常,再次看一眼异常栈,如下图:

image.png

网络异常栈

我们可以看Wireshark 的包信息如下图,

image.png

可以看到,我们来看一下流程,第一步是区分服务端和客户端:

  • 服务端:IP后缀为132 ,端口20004是,

  • 客户端:IP后缀为156,端口为4528。

前三行是三次握手,没有问题,但是最后的四次挥手这里有问题(可以对照着四次挥手的图看):

  • No.189 行:服务端发起FIN 报文希望关闭连接,服务端进入FIN_WAIT1 状态;

  • No.190 行:客户端响应服务端的FIN 报文发送ACK 报文,进入CLOSE_WAIT 状态;

  • No.191 ~ No.197:服务端接收到客户端的ACK 进入FIN_WAIT2 状态,此时服务端是不接收数据传输的,但是我们可以看到Wireshark 191 ~ 196 行客户端还在发送数据报文,正常应该是客户端发送FIN 报文关闭连接,让服务端进入TIME_WAIT 状态,但是客户端没有发送FIN报文,而是向已经准备关闭的连接通道中发送了数据报文,因为服务端不认你客户端的数据,所以发送了RST 信号报文来重置连接。

下面是正常的四次挥手和异常的四次挥手对照图:

image.png

对比

4.2 问题定位

===========================================================================

  1. 供应商接口查看状态让HTTP 请求的被调用方(供应商)查看当前网络状态,Linux 命令如下所示:1netstat -n | awk **’/^tcp/ {++state[$NF]} END {for(key in state) print key,“\t”,state[key]}’**结果如下:image-20200522214047234可以对照着四次挥手流程图看状态:ESTABLISHED: 处于连接建立状态的连接数FIN_WAIT1: 处于连接关闭FIN_WAIT1 状态的连接数FIN_WAIT2: 处于连接关闭FIN_WAIT2 状态的连接数TIME_WAIT:处于 TIME_WAIT 状态的连接数可以看到TIME_WAIT 状态很多,这个是正常的,只要记住,正常四次挥手流程中,主动关闭的一方会经过TIME_WAIT 状态,被动关闭一方会经过 CLOSE_WAIT 状态,这二个状态(TIME_WAIT & CLOSE_WAIT)需要做个区分,如果 CLOSE_WAIT 状态过多可能会有问题,这个我会在扩展阅读详细说,继续分析异常。

  2. 确认连接方式通过上面Wireshark 异常包可以知道是服务端进入FIN_WAIT2 状态后,客户端继续发送数据包,导致服务端RST 连接。这里有二个问题:

  3. 为什么服务端主动发起FIN 关闭连接呢?

  4. 为什么客户端在接收到服务端的FIN 并回复ACK 报文之后,为什么没有发送FIN 关闭连接报文呢?不卖关子了,讲了这么多吊足了玩家们的胃口,真实原因都不好意思讲了,其实是因为供应商不支持长连接,但是我们为了资源复用,降低HTTP 连接创造销毁的开销,使用了连接池,连接池的连接是复用的,是长连接,所以才会出现服务端第二次发送数据时不进行三次握手,而是直接传输数据报文的情况。

5. 代码修复:调整客户端代码

===================================================================================

5.1 HttpClient 长连接改为短连接

==========================================================================================

  1. 服务端那边配置的Nginx 是短连接方式,客户端代码使用长连接,客户端HTTP 请求的代码(删除了部分代码),请求结束之后,连接不进行关闭,连接池复用连接。如下:长连接主要代码如果要支持长连接,服务端Nginx 配置需要做些修改,如下: 1http{

2 keepalive_timeout 60s; #连接保持的超时时间

3

4 upstream servers {

5 keepalive 10;

6 }

7

8 server {

9 listener 20004 so_keepalive=on #这个是支持长连接的配置

10 }

11}

  1. 代码问题修复客户端连接方式从长连接改成短连接,设置连接不复用就可以了,如下:image-20200525203700284

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

最后

2020年在匆匆忙忙慌慌乱乱中就这么度过了,我们迎来了新一年,互联网的发展如此之快,技术日新月异,更新迭代成为了这个时代的代名词,坚持下来的技术体系会越来越健壮,JVM作为如今是跳槽大厂必备的技能,如果你还没掌握,更别提之后更新的新技术了。

更多JVM面试整理:

…(img-AfzoOoLP-1710419488676)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-tS11B9S3-1710419488677)]

最后

2020年在匆匆忙忙慌慌乱乱中就这么度过了,我们迎来了新一年,互联网的发展如此之快,技术日新月异,更新迭代成为了这个时代的代名词,坚持下来的技术体系会越来越健壮,JVM作为如今是跳槽大厂必备的技能,如果你还没掌握,更别提之后更新的新技术了。

[外链图片转存中…(img-BvtIe4qp-1710419488677)]

更多JVM面试整理:

[外链图片转存中…(img-5kqN3ISc-1710419488678)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值