了解完WireShark抓包工具的使用,现在整理下TCP三次握手/四次挥手。
1、WireShark介绍
1.1 客户端界面
打开WireShark之后,能看到三个区域。最上方是工具栏,可以开始捕获、停止捕获等操作;中间是捕获过滤器,能够在开始捕获前指定过滤规则;下方就是捕获的网络设备了,双击其中一个网络设备后就开始进行网络流浪捕获了。
结果的展示主要分三个区域,最上方是请求和响应列表,每一条记录表示一次请求或响应的交互。中间是对选中的交互解析后的结果。最下方是原始的数据格式。
在请求列表上方,我们可以指定过滤器,用于筛选已经捕获到的数据。
1.2 过滤器的使用
待补充
1.3 界面上一些小TIPS
左边的实线连起来的表示同一次会话发生的各个阶段。
2、使用Wireshark分析TCP三次握手过程
2.1 三次握手原理
先来看下基本的原理
第一次握手:建立起连接时,客户端发送SYN到服务器,并进入了SYN_SENT状态。
第二次握手:服务器接收到请求后,回送SYN+ACK信令到客户端,此时服务器进入了SYN_RRCVD状态。
第三次握手:客户端收到了SYN+ACK包,向服务器发送确认ACK包,客户端进入了ESTABLISHED状态,服务器收到请求后也进入了ESTABLISHED状态,完成三次握手,此时TCP连接成功,客户端与服务器开始传送数据。
TCP报文结构
序列号&确认号:
- 序列号:序列号 (Sequence Numbe) 发送数据包中的第一个字节的,32 位。TCP 协议是基于流发送数据的,所有使用序号给发送的所有数据字节都编上号,并按照序号进行顺序发送。
- 确认号:确认序列号 (Acknowledgment Number),32 位。TCP 的通信是全双工的(两端同时发送和接受),当连接建立成功后,每一方都能同时发送和接收数据。每一个方向的序号代表这个报文段携带的第一个字节数据的编号。每一方还需要使用确认号对它已经收到的字节进行确认。即:本端把收到的最后一个字节的编号加一,这个值就是确认号,然后发送给对端。例如,B正确收到了A发送过来的一个报文段,其序号字段为501,数据长度为200,则序号为(501~700),这表明B正确接受了A发送到700为止的数据,因此B的期望收到的下一个数据序号为701。
标志位的作用:
- URG(紧急指针):该位用于指示紧急数据的存在。当设置了这个标志位时,紧急指针字段中的数值才有效。
- ACK(确认):表示确认号字段中包含的序号是有效的,用于表明“确认应答”中的确认号字段值是有效的。这样一来,就可以确保双方都正确地理解对方的发送数据。
- PSH(推送):该标志位用于通知接收端立即交付数据给应用层而不是等待填充缓冲区再交付。
- RST(复位):当连接出现问题时,可以通过设置这个标志位来强制关闭TCP连接,从而清除连接状态。通常用于拒绝一个非法的报文或者重新建立连接。
- SYN(同步):用于在建立连接时进行同步序号的同步。在连接建立时,客户端发送一个带有SYN标志位的数据包给服务器,服务器收到后回传一个带有SYN/ACK标志位的数据包,并等待客户端的确认。
- FIN(结束):用于释放一个已经建立的连接。当一方要断开连接时,会向对方发送一个带有FIN标志位的数据包,对方收到后可以回传一个带有ACK标志位的确认包,也可以直接发送一个带有FIN/ACK标志位的数据包。
- 窗口:窗口(Window)指的是发送本报文段的一方的接收窗口,窗口值告诉对方:从本报文段的确认号算起,接收方目前允许对方发送的数量。窗口值作为接收方让发送方设置其发送窗口的依据。 例如:A发送一个报文段,其确认号是701,窗口字段是1000,就是告诉对方:“从701开始,我还可以接收1000个字节,即701-1700
- 校验和:校验和(Checksum),16 位。每一个报文段都包括有校验和字段,若报文有损伤,则由终点 TCP 将其丢弃,并被认为是丢失。TCP 的校验和是强制的。
- 紧急指针:紧急指针(Urgent Pointers),16 位,只有 URG 标志位被设值时该字段才有意义,表示紧急数据相对序列号(Sequence Number字段的值)的偏移。用URG 值 + 序号得到最后一个紧急字节。
- 选项:选项(长度可变,最长可以为40个字节),这里重点说说MSS。
(1)TCP在建立连接的时候,就可以确定发送数据包的单位MSS(Maximum Segment Size), MSS是每一个TCP报文段中的数据字段的最大长度。 (注意,不是整个TCP报文段的最大长度)。
(2)TCP在传输大量数据的时候,是以MSS大小将数据进行分割发送的,重传时也是以MSS为单位。
为什么要设计一个MSS呢?
我们知道,TCP报文端的数据部分,至少还要加上40字节的首部(20字节TCP头+20字节IP头),如果选择了很小的MSS长度,比如只有一个字节的数据,却要封装上40字节的头,那么网络的利用率就很低了。但是如果设置得非常大,在IP层传输时就可能被拆分成多个数据片,在终点还要重新拼接,传输出错时还要重传,这样也会使得开销增大。 理想状态下是MSS正好是IP不会分片的最大数据长度。
MSS是在TCP连接建立时,由两边的主机计算出来的:两端的主机都会将自己能够支持的MSS写入。 如果没有指定,就是默认的536字节长。否则就选用较小的值。
2.2 第一次握手
第一次握手:客户端建立连接,向服务器发送连接请求(客户端发送SYN到服务器),并进入了SYN_SENT状态。
SYN :标志位,表示请求建立连接。
Seq = 0 :初始建立连接值为0,数据包的相对序列号从0开始,表示当前还没有发送数据。
Ack =0:初始建立连接值为0,已经收到包的数量,表示当前没有接收到数据。
WIN = 8192 来自Window size: 8192。
MSS = 1460 来自 Maximum segment size: 1460 byte ,最长报文段,TCP包所能携带的最大数据量,不包含TCP头和Option。一般为MTU值减去IPv4头部(至少20字节)和TCP头部(至少20字节)得到。
WS = 4 来自windows scale : 2 (multiply by 4): 窗口扩张,放在TCP头之外的Option,向对方声明一个shift count,作为2的指数,再乘以TCP定义的接收窗口,得到真正的TCP窗口。
2.3 第二次握手
第二次握手:服务器接收到请求,回送SYN+ACK信令到客户端(连接请求确认),此时服务器进入了SYN_RCVD状态。
Seq = 0 :初始建立值为0,表示当前还没有发送数据
Ack = 1 : 表示当前端成功接收的数据位数,虽然客户端没有发送任何有效数据,确认号还是被加1,因为包含SYN或FIN标志位。尽管客户端没有发送任何有效数据,确认号还是被加1,这是因为接收的包中包含SYN或FIN标志位(并不会对有效数据的计数产生影响,因为含有SYN或FIN标志位的包并不携带有效数据。
2.4 第三次握手
第三次握手:客户端收到SYN+ACK包,向服务器发送确认ACK包,客户端进入了ESTABLISHED状态,服务器接收到请求后,也进入了ESTABLISHED状态,完成了三次握手,此时TCP连接成功,客户端与服务器可以开始传输数据了。
ACK :标志位,表示已经收到记录
Seq = 1 :表示当前已经发送1个数据
Ack = 1 : 表示当前端成功接收的数据位数,虽然客户端没有发送任何有效数据,确认号还是被加1,因为包含SYN或FIN标志位。尽管客户端没有发送任何有效数据,确认号还是被加1,这是因为接收的包中包含SYN或FIN标志位(并不会对有效数据的计数产生影响,因为含有SYN或FIN标志位的包并不携带有效数据)
2.5 为什么是三次握手
这个问题的本质是, 信道不可靠, 但是通信双发需要就某个问题达成一致. 而要解决这个问题, 无论你在消息中包含什么信息, 三次通信是理论上的最小值. 所以三次握手不是TCP本身的要求, 而是为了满足”在不可靠信道上可靠地传输信息”这一需求所导致的.
也是为了最小的代价验证会话双方的收发功能正常:
- 第一次握手成功:说明客户端的数据可以被服务端收到,说明客户端的发功能可用,说明服务端的收功能可用。但客户端自己不知道数据是否被接收。
- 第二次握手成功:说明服务端的数据可以被客户端收到,说明服务端的发功能可用,说明客户端的收功能可用。同时客户端知道自己的数据已经正确到达服务端,自己的发功能正常。但是服务端自己不知道数据是否被接收。
- 第三次握手成功:说明服务端知道自己的数据已经正确到达客户端端,自己的发功能正常。至此服务成功建立。
2.6 常见的面试题总结
以下整理来自二哥的Java进阶之路
2.6.1 TCP 握手为什么是三次,为什么不能是两次?不能是四次?
使用三次握手可以建立一个可靠的连接。这一过程的目的是确保双方都知道对方已准备好进行通信,并同步双方的序列号,从而保持数据包的顺序和完整性。
为什么TCP握手不能是两次?
- 为了防止服务器一直等待,等到黄花菜都凉了。
- 为了防止客户端已经失效的连接请求突然又传到服务器。
要知道,网络传输是有延时的(要通过网络光纤、WIFI、卫星信号传输等)。
假如说客户端发起了 SYN=1 的第一次握手。服务器也及时回复了 SYN=2 和 ACK=1 的第二次握手,但是这个 ACK=1 的确认报文段因为某些原因在传输过程中丢失了。
如果没有第三次握手告诉服务器,客户端收到了服务器的回应,那服务器是不知道客户端有没有接收到的。于是服务器就一直干巴巴地开着端口在等着客户端发消息呢,但其实客户端并没有收到服务器的回应,心灰意冷地跑了。
还有一种情况是,一个旧的、延迟的连接请求(SYN=1)被服务器接受,导致服务器错误地开启一个不再需要的连接。
举个例子:假设你(客户端)给你的朋友(服务器)发送了一个邮件(连接请求)。因为某些原因,这封邮件迟迟没有到达朋友那里,可能是因为邮局的延误。于是你决定再发一封新的邮件。朋友收到了第二封邮件,你们成功地建立了连接并开始通信。
但是,过了很久,那封延误的旧邮件突然也到了你朋友那里。如果没有一种机制来识别和处理这种延误的邮件,你的朋友可能会以为这是一个新的连接请求,并尝试响应它,但其实你已经重新发了请求,原来的不需要了。这就导致了不必要的混乱和资源浪费。
所以我们需要“三次握手”来确认这个过程:
- 第一次握手:客户端发送 SYN 包(连接请求)给服务器,如果这个包延迟了,客户端不会一直等待,它可能会重试并发送一个新的连接请求。
- 第二次握手:服务器收到 SYN 包后,发送一个 SYN-ACK 包(确认接收到连接请求)回客户端。
- 第三次握手:客户端收到 SYN-ACK 包后,再发送一个 ACK 包给服务器,确认收到了服务器的响应。
为什么不是四次?
三次握手已经足够创建可靠的连接了,没有必要再多一次握手。
什么是泛洪攻击?
泛洪攻击(SYN Flood Attack)是一种常见的 DoS(拒绝服务)攻击,攻击者会发送大量的伪造的 TCP 连接请求,导致服务器资源耗尽,无法处理正常的连接请求。
半连接服务拒绝,也称为 SYN 洪泛攻击或 SYN Flood。
所谓的半连接就是指在 TCP 的三次握手过程中,当服务器接收到来自客户端的第一个 SYN 包后,它会回复一个 SYN-ACK 包,此时连接处于“半开”状态,因为连接的建立还需要客户端发送最后一个 ACK 包。
在收到最后的 ACK 包之前,服务器会为这个尚未完成的连接分配一定的资源,并在它的队列中保留这个连接的位置。
2.6.2 三次握手中每一次没收到报文会发生什么情况?
- 第一次握手服务端未收到SYN报文
服务端不会进行任何的动作,而客户端由于一段时间内没有收到服务端发来的确认报文,等待一段时间后会重新发送SYN报文,如果仍然没有回应,会重复这个过程,直到发送次数超过最大重传次数限制,就会返回连接建立失败。
- 第二次握手客户端未收到服务端响应的SYN+ACK信令
客户端会继续重传,直到次数限制;而服务端此时会阻塞在accept()处,等待客户端发送ACK报文。
- 第三次服务端未收到客户端发送的ACK报文
服务端同样会采用类似客户端的超时重传机制,如果重试次数超过限制,则 accept()调用返回-1,服务端建立连接失败;而此时客户端认为自己已经建立连接成功,因此开始向服务端发送数据,但是服务端的 accept()系统调用已经返回,此时不在监听状态,因此服务端接收到客户端发送来的数据时会发送 RST 报文给客户端,消除客户端单方面建立连接的状态。
2.6.3 第二次握手传回了ACK,为什么还要传回SYN?
ACK是为了告诉客户端传来的数据已经接收无误,而传回SYN是为了告诉客户端,服务端响应的确实是客户端发送的报文。
2.6.4 第三次握手可以携带数据吗?
第三次是可以携带数据的。
此时客户端处于ESTABLISHED状态。对于客户端来说,它已经建立连接成功,并且确认服务端的接收和发送能力是正常的。
第一次握手不能携带数据是处于安全考虑,因为如果允许携带数据,攻击者每次在SYN报文中携带大量数据,就会导致服务端消耗更多的时间和空间处理这些报文,会造成CPU和内存的消耗。
2.6.5 说说半连接队列和SYN Flood 攻击的关系?
什么是半连接队列?
TCP进入三次握手前,服务端会从CLOSE状态变为LISTEN状态,同时在内部创建了两个队列:半连接队列和全连接队列。
三次握手中创建的队列
顾名思义,半连接队列存放的是三次握手未完成的连接,全连接队列存放的是完成三次握手的连接。
- TCP三次握手时,客户端发送SYN到服务端,服务端收到之后,回复了SYN+ACK,状态有LISTEN改边未SYN_RCVD,此时这个连接就会被推入了SYN队列,即半连接队列。
- 当客户端回复ACK,服务端接收后,三次握手就完成了。这时连接会等待被具体的应用取走,在被取走之前,它被推入ACCEPT队列,即全连接队列。
什么是SYN Flood?
SYN Flood 是一种典型的DDos攻击,它在短时间内,伪造不存在的IP地址,向服务器发送大量SYN报文。当服务器回复SYN+ACK报文后,不会收到ACK回应报文,那么SYN队列里的连接就不会出队列,久而久之就会占满服务端的SYN接收队列(半连接队列),使得服务器不能为正常用户服务。
那有什么对应方案吗?
主要有syn cookie 和 SYN Proxy防火墙等。
一个连接的建立需要进行TCP三次握手,如下图所示
- syn cookie:在收到 SYN 包后,服务器根据一定的方法,以数据包的源地址、端口等信息为参数计算出一个 cookie 值作为自己的 SYN+ACK 包的序列号,回复 SYN+ACK 后,服务器并不立即分配资源进行处理,等收到发送方的 ACK 包后,重新根据数据包的源地址、端口计算该包中的确认序列号是否正确,如果正确则建立连接,否则丢弃该包。
- SYN Proxy 防火墙:服务器防火墙会对收到的每一个 SYN 报文进行代理和回应,并保持半连接。等发送方将 ACK 包返回后,再重新构造 SYN 包发到服务器,建立真正的 TCP 连接。
关闭连接时,客户端向服务端发送FIN时,仅仅表示客户端不再发送数据了但是还能接收数据。
2.6.6 保活计时器有什么作用?
除时间等待器外,TCP还有一个保活计时器(keepalive timer)。
设想这样的场景:客户已主动与服务器建立了 TCP 连接。但后来客户端的主机突然发生故障。显然,服务器以后就不能再收到客户端发来的数据。因此,应当有措施使服务器不要再白白等待下去。这就需要使用保活计时器了。
服务器每收到一次客户端的数据,就重新设置保活计时器,时间的设置通常是两个小时。若两个小时都没有收到客户端的数据,服务端就发送一个探测报文段,以后则每隔 75 秒钟发送一次。若连续发送 10 个探测报文段后仍然无客户端的响应,服务端就认为客户端出了故障,接着就关闭这个连接。
2.6.7 说说TCP报文头部的格式?
详解查看:TCP协议特点及报文格式详解_tcp报文格式详解-优快云博客
2.6.8 TCP如何保证可靠性的?
TCP 主要提供了检验和、序列号/确认应答、超时重传、最大消息长度、滑动窗口控制等方法实现了可靠性传输。
- 连接管理:TCP使用了三次握手和四次挥手保证可靠地建立连接和释放连接,这里就不用多说了。
- 校验和:TCP 将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果接收端的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。
- 序列号/确认应答:TCP 给发送的每一个包进行编号,接收方会对收到的包进行应答,发送方就会知道接收方是否收到对应的包,如果发现没有收到,就会重发,这样就能保证数据的完整性。就像老师上课,会问一句,这一章听懂了吗?没听懂再讲一遍。
- 流量控制:TCP 连接的每一方都有固定大小的缓冲空间,TCP 的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。(TCP 利用滑动窗口实现流量控制)
滑动窗口简图
- 最大消息长度:在建立 TCP 连接的时候,双方约定一个最大的长度(MSS)作为发送的单位,重传的时候也是以这个单位来进行重传。理想的情况下是该长度的数据刚好不被网络层分块。
最大消息长度
- 超时重传:超时重传是指发送出去的数据包到接收到确认包之间的时间,如果超过了这个时间会被认为是丢包了,需要重传。
超时重传
- 拥塞控制:如果网络非常拥堵,此时再发送数据就会加重网络负担,那么发送的数据段很可能超过了最大生存时间也没有到达接收方,就会产生丢包问题。为此 TCP 引入慢启动机制,先发出少量数据,就像探路一样,先摸清当前的网络拥堵状态后,再决定按照多大的速度传送数据。
拥塞控制简略示意图
2.6.9 说说TCP的流量控制?
TCP 提供了一种机制,可以让发送端根据接收端的实际接收能力控制发送的数据量,这就是流量控制。
TCP 通过滑动窗口来控制流量,我们看下简要流程:
- 首先双方三次握手,初始化各自的窗口大小,均为 400 个字节。
TCP 流量控制
- 假如当前发送方给接收方发送了 200 个字节,那么,发送方的
SND.NXT
会右移 200 个字节,也就是说当前的可用窗口减少了 200 个字节。 - 接受方收到后,放到缓冲队列里面,REV.WND =400-200=200 字节,所以 win=200 字节返回给发送方。接收方会在 ACK 的报文首部带上缩小后的滑动窗口 200 字节
- 发送方又发送 200 字节过来,200 字节到达,继续放到缓冲队列。不过这时候,由于大量负载的原因,接受方处理不了这么多字节,只能处理 100 字节,剩余的 100 字节继续放到缓冲队列。这时候,REV.WND = 400-200-100=100 字节,即 win=100 返回发送方。
- 发送方继续发送 100 字节过来,这时候,接收窗口 win 变为 0。
- 发送方停止发送,开启一个定时任务,每隔一段时间,就去询问接受方,直到 win 大于 0,才继续开始发送。
2.6.10 详细说说TCP的滑动窗口?
TCP 发送一个数据,如果需要收到确认应答,才会发送下一个数据。这样的话就会有个缺点:效率会比较低。
“用一个比喻,我们在微信上聊天,你打完一句话,我回复一句之后,你才能打下一句。假如我没有及时回复呢?你是把话憋着不说吗?然后傻傻等到我回复之后再接着发下一句?”
为了解决这个问题,TCP 引入了窗口,它是操作系统开辟的一个缓存空间。窗口大小值表示无需等待确认应答,而可以继续发送数据的最大值。
TCP 头部有个字段叫 win,也即那个 16 位的窗口大小,它告诉对方本端的 TCP 接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度,从而达到流量控制的目的。
“通俗点讲,就是接受方每次收到数据包,在发送确认报文的时候,同时告诉发送方,自己的缓存区还有多少空余空间,缓冲区的空余空间,我们就称之为接受窗口大小。这就是 win。”
TCP 滑动窗口分为两种: 发送窗口和接收窗口。发送端的滑动窗口包含四大部分,如下:
- 已发送且已收到 ACK 确认
- 已发送但未收到 ACK 确认
- 未发送但可以发送
- 未发送也不可以发送
发送端滑动窗口
- 深蓝色框里就是发送窗口。
- SND.WND: 表示发送窗口的大小, 上图虚线框的格子数是 10 个,即发送窗口大小是 10。
- SND.NXT:下一个发送的位置,它指向未发送但可以发送的第一个字节的序列号。
- SND.UNA: 一个绝对指针,它指向的是已发送但未确认的第一个字节的序列号。
接收方的滑动窗口包含三大部分,如下:
- 已成功接收并确认
- 未收到数据但可以接收
- 未收到数据并不可以接收的数据
接收方滑动窗口
- 蓝色框内,就是接收窗口。
- REV.WND: 表示接收窗口的大小, 上图虚线框的格子就是 9 个。
- REV.NXT: 下一个接收的位置,它指向未收到但可以接收的第一个字节的序列号。
2.6.11 了解 Nagle 算法和延迟确认吗?
Nagle 算法和延迟确认是干什么的?
当我们 TCP 报⽂的承载的数据⾮常⼩的时候,例如⼏个字节,那么整个⽹络的效率是很低的,因为每个 TCP 报⽂中都会有 20 个字节的 TCP 头部,也会有 20 个字节的 IP 头部,⽽数据只有⼏个字节,所以在整个报⽂中有效数据占有的比例就会⾮常低。
小数据情况
这就好像快递员开着⼤货⻋送⼀个⼩包裹⼀样浪费。
那么就出现了常⻅的两种策略,来减少⼩报⽂的传输,分别是:
- Nagle 算法
- 延迟确认
Nagle 算法
Nagle 算法:任意时刻,最多只能有一个未被确认的小段。所谓 “小段”,指的是小于 MSS 尺寸的数据块,所谓 “未被确认”,是指一个数据块发送出去后,没有收到对方发送的 ACK 确认该数据已收到。
Nagle 算法的策略:
- 没有已发送未确认报⽂时,⽴刻发送数据。
- 存在未确认报⽂时,直到「没有已发送未确认报⽂」或「数据⻓度达到 MSS ⼤⼩」时,再发送数据。
只要没满⾜上⾯条件中的⼀条,发送⽅⼀直在囤积数据,直到满⾜上⾯的发送条件。
延迟确认
事实上当没有携带数据的 ACK,它的⽹络效率也是很低的,因为它也有 40 个字节的 IP 头 和 TCP 头,但却没有携带数据报⽂。
为了解决 ACK 传输效率低问题,所以就衍⽣出了 TCP 延迟确认。
TCP 延迟确认的策略:
- 当有响应数据要发送时,ACK 会随着响应数据⼀起⽴刻发送给对⽅
- 当没有响应数据要发送时,ACK 将会延迟⼀段时间,以等待是否有响应数据可以⼀起发送
- 如果在延迟等待发送 ACK 期间,对⽅的第⼆个数据报⽂⼜到达了,这时就会⽴刻发送 ACK
一般情况下,Nagle 算法和延迟确认不能一起使用,Nagle 算法意味着延迟发,延迟确认意味着延迟接收,两个凑在一起就会造成更大的延迟,会产生性能问题。
2.6.12 说说 TCP 的拥塞控制?
。。。。。。
3、分析四次挥手过程
3.1 理论基础
TCP 连接的断开过程被形象地概括为四次挥手。
第一次挥手:客户端向服务器发送一个 FIN 结束报文,表示客户端没有数据要发送了,但仍然可以接收数据。客户端进入 FIN-WAIT-1 状态。
第二次挥手:服务器接收到 FIN 报文后,向客户端发送一个 ACK 报文,确认已接收到客户端的 FIN 请求。服务器进入 CLOSE-WAIT 状态,客户端进入 FIN-WAIT-2 状态。
第三次挥手:服务器向客户端发送一个 FIN 报文,表示服务器也没有数据要发送了。服务器进入 LAST-ACK 状态。
第四次挥手:客户端接收到 FIN 报文后,向服务器发送一个 ACK 报文,确认已接收到服务器的 FIN 请求。客户端进入 TIME-WAIT 状态,等待一段时间以确保服务器接收到 ACK 报文。服务器接收到 ACK 报文后进入 CLOSED 状态。客户端在等待一段时间后也进入 CLOSED 状态。
3.2 常见的面试总结
3.2.1 TCP挥手为什么需要四次呢?
- 再来回顾下四次挥手双方发FIN包的过程,就能理解为什么需要四次了。
- 服务端收到客户端的FIN报文时,先回一个ACK的应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN报文给客户端来表示同意关闭连接。
从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK
和 FIN
一般都会分开发送,从而比三次握手导致多了一次。
3.2.2 TCP 四次挥手过程中,为什么需要等待 2MSL, 才进入 CLOSED 关闭状态?
为什么需要等待?
- 为了保证客户端发送的最后一个ACK报文段能够到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一但这个ACK丢失的话,服务器就无法正常的进入关闭连接状态。
- 他还可以防止已失效的报文段。客户端在发送最后一个ACK之后,再经过经过2MSL,就可以使本链接持续时间内所产生的所有报文段都从网络中消失。从保证在关闭连接后不会有还在网络中滞留的报文段去骚扰服务器。
注意:在服务器发送了FIN-ACK之后,会立即启动超时重传计时器。客户端在发送最后一个ACK之后会立即启动时间等待计时器。
为什么等待的时间是2MSL
MSL 是 Maximum Segment Lifetime,报⽂最⼤⽣存时间,它是任何报⽂在⽹络上存在的最⻓时间,超过这个时间报⽂将被丢弃。
TIME_WAIT 等待 2 倍的 MSL,⽐较合理的解释是:⽹络中可能存在来⾃发送⽅的数据包,当这些发送⽅的数据包被接收⽅处理后⼜会向对⽅发送响应,所以⼀来⼀回需要等待 2 倍的时间。
3.2.3 CLOSE_WAIT和TIME_WAIT的状态和意义?
CLOSE_WAIT状态有什么意义?
服务端收到客户端关闭连接的请求并确认后,就会进入CLOSE_WAIT状态。此时服务端可能还有一些未处理完的数据,因此不能立即关闭连接,而CLOSE_WAIT状态就是为了保证服务端关闭连接之前将待发送的数据处理完。
TIME_WAIT有什么意义?
TIME_WAIT发生在第四挥手,当客户端在发送ACK确认对方的FIN报文后,会进入TIME_WAIT状态。
它存在的意义主要有两个:
- 在TIME_WAIT状态中,客户端可以重新发送ACK确保对方正常关闭连接。
- 在TIME_WAIT持续的2MSL时间后,确保旧数据包完全消失,避免它们干扰未来建立的新连接。
补充:MSL(Maximum Segment Lifetime):TCP 报文段在网络中的最大存活时间,通常为 30 秒到 2 分钟
3.2.4 TIME_WAIT状态过多会导致什么问题?怎么解决?
TIME_WAIT状态过多会导致什么问题?
如果服务器有处于TIME_WAIT状态的TCP,则说明是由服务器方主动发起的断开请求。过多的TIME_WAIT状态主要的危害有两种:
- 内存资源占用。
- 对端口资源的占用,一个TCP连接至少消耗一个本地接口。
怎么解决TIME_WAIT状态过多?
- 服务器设置SO_REUSEADDR套接字来通知内核,如果端口被占用,但是TCP连接位于TIME_WAIT状态时可以重用端口。
- 还可以使用长连接的方式来减少TCP的连接和断开,在长连接的业务里往往不需要考虑TIME_WAIT状态。
参考:
wireShark TCP 抓包 wireshark抓包实例教程_mob64ca13fa2f9e的技术博客_51CTO博客
(图文并茂,权威最详细)Wireshark抓包分析 TCP三次握手/四次挥手详解-腾讯云开发者社区-腾讯云
TCP协议特点及报文格式详解