天下没有不散的宴席,对于 TCP 连接也是这样, TCP 断开连接是通过四次挥手方式。下面我们通过实操,来彻底理解四次挥手。
对TCP连接建立三次握手感兴趣的同学,可以看我上一篇文章:一文带你读懂:TCP连接的三次握手和四次挥手(上篇)
winter
必须先提及几个基础概念:TCP四元组、TCP协议簇、TCP协议报文。
TCP协议簇
TCP四元组
(1)源地址和目的地址的字段(32 位)是在 IP 头部中,作用是通过 IP 协议发送报文给对方主机。
(2)源端口和目的端口的字段(16 位)是在 TCP 头部中,作用是告诉 TCP 协议应该把报文发给哪个进程。见下图所示:
TCP协议报文 = TCP首部 + TCP荷载。
我们通过 curl 112.47.52.137 的抓包记录来分析一下这个TCP报文。
完整报文如下:
下面挑选一些跟三次握手和四次挥手,紧密相关的组成字段来解析:
TCP序列号(Sequence Number)
定义:TCP会话的每一端都包含一个32位(bit)的序列号(可能是0和4,294,967,295之间的任意值),该序列号被用来跟踪该端发送的数据量,每一个包中都包含序列号,在接收端则通过确认号用来通知发送端数据成功接收。
作用:在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
TCP确认号(Acknowledgment Number)
指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
TCP控制位
TCP在其协议头中使用大量的标志位或者说1位(bit)布尔域来控制连接状态,一个包中有可以设置多个标志位。
TCP控制位即TCP标志位,有6种标示:
SYN(synchronous建立联机)
ACK(acknowledgement 确认)
PSH(push传送)
FIN(finish结束)
RST(reset重置)
URG(urgent紧急)
上面6个控制位里,最常见的是四个标志位:
ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1 。
RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
SYN:该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。
四次挥手
4次挥手是指发送了4个报文段,四次挥手图例:
解析:
TCP连接是双向传输的对等的模式,就是说双方都可以同时向对方发送或接收数据。
当有一方要关闭连接时,会发送指令告知对方,我要关闭连接了。这时对方会回一个ACK,此时一个方向的连接关闭。
但是另一个方向仍然可以继续传输数据,等到发送完了所有的数据后,会发送一个FIN段来关闭此方向上的连接。
接收方发送ACK确认关闭连接。注意,接收到FIN报文的一方只能回复一个ACK, 它是无法马上返回对方一个FIN报文段的,因为结束数据传输的“指令”是上层应用层给出的,我只是一个“搬运工”,我无法了解“上层的意志”
。
四次挥手与三次挥手?
虽然我们在学习 TCP 挥手时,学到的是需要四次来完成 TCP 挥手,但是在一些情况下, TCP 四次挥手是可以变成 TCP 三次挥手的。
在用 wireshark 工具抓包的时候,我们也会常看到 TCP 挥手过程是三次,而不是四次,如下图:第二、三次挥手可以合并。
第一次挥手
第二次挥手、第三次挥手
第四次挥手
FAQ
1、什么情况会出现三次挥手?
当被动关闭方(上图的服务端)在 TCP 挥手过程中,「没有数据要发送」并且「开启了 TCP 延迟确认机制」,那么第二和第三次挥手就会合并传输,这样就出现了三次挥手。
然后因为 TCP 延迟确认机制是默认开启的,所以导致我们抓包时,看见三次挥手的次数比四次挥手还多。
2、为什么最后一次挥手,它的 TIME_WAIT 等待的时间是 2MSL?
MSL
是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个 TTL
字段,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。
TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是:网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间。
比如,如果被动关闭方没有收到断开连接的最后的 ACK 报文,就会触发超时重发 FIN
报文,另一方接收到 FIN 后,会重发 ACK 给被动关闭方, 一来一去正好 2 个 MSL。
可以看到 2MSL时长 这其实是相当于至少允许报文丢失一次。比如,若 ACK 在一个 MSL 内丢失,这样被动方重发的 FIN 会在第 2 个 MSL 内到达,TIME_WAIT 状态的连接可以应对。
注意:在 Linux 系统里 2MSL
默认是 60
秒,那么一个 MSL
也就是 30
秒。Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。
3、为什么需要 TIME_WAIT 状态?
主动发起关闭连接的一方,才会有 TIME-WAIT 状态。
4、什么是 TCP 延迟确认机制?
当发送没有携带数据的 ACK,它的网络效率也是很低的,因为它也有 40 个字节的 IP 头 和 TCP 头,但却没有携带数据报文。为了解决 ACK 传输效率低问题,所以就衍生出了 TCP 延迟确认。TCP 延迟确认的策略:
当有响应数据要发送时,ACK 会随着响应数据一起立刻发送给对方
当没有响应数据要发送时,ACK 将会延迟一段时间,以等待是否有响应数据可以一起发送(很明显,上述实例就命中了这种情况)
如果在延迟等待发送 ACK 期间,对方的第二个数据报文又到达了,这时就会立刻发送 ACK
往期推荐
《网络基础》
《经典书籍》
带你读懂《Java并发编程》:第3章 助于线程安全的三剑客:final & volatile & 线程封闭
一文读懂《Java并发编程实战》:第2章 影响线程安全性的原子性和加锁机制
一文读懂《Java并发编程实战》:第1章 多线程安全性与风险
《Mysql技术》
记录一次Mysql死锁事件(由Insert与uniqueKey导致)
《性能解决方案》
《架构设计》
《感悟》