这是个很经典的问题,TCP断开连接时的四次挥手中,客户端在发送最后的ACK后要进入TIME-WAIT状态,这个状态时长为2倍MSL。
MSL是Maximum Segment Lifetime的英文缩写,可译为“最长报文寿命”,它是任何报文在网络上存在的最长的最长时间,超过这个时间报文将被丢弃。
TIME-WAIT时长为2 MSL这主要有两个原因
- 尽量让服务端收到最后的ACK
- 确保当前连接的报文不会出现在下一次连接中
参考《Unix 网络编程 卷1(第三版)》,专业一点的说法是
- 可靠地实现TCP全双工连接的终止
- 允许老的重复分节在网络中消逝
以下段落假定在一次四次挥手中,主动方为客户端,被动方为服务端。
分析
原因2
我们先说原因2。为什么要先说原因2?因为原因2更容易理解,也更明确。我们知道TCP是面向字节流、面向连接的。但是假如你对计算机网络课程前几章的有记忆,你就知道,TCP之下的网络层并不使用电路交换,而是使用分组交换。换言之,即使TCP是面向连接的,底层实现仍然是一个个分散的数据包、报文。
TCP大致通过一个四元组来标记一条连接,分别是:源IP、目的IP、源端口、目的端口。假设在一次连接中,部分报文因为某种原因滞留在路由网络中,然后这次连接断开了,再然后客户复用了端口,开启了和当前服务器一次新的连接。此时可以看见,因为客户端复用了端口,而且客户端和服务器短期内IP都不会变,而且服务器通常只会又有一个端口监听。因此当前连接的四元组和上次连接一样。此时假如上一次连接中滞留的报文又恢复传播,那就会进入当前连接中,因为这些报文本不属于本次连接,这可能造成数据混乱。实际上由于序号机制的存在,这种情况发生几率较小,但是显然还是有必要避免。
参考TCP四次挥手图。已知每个报文最多只有1 MSL寿命,因此,我们可以想当然的猜想,客户端发送了最后的ACK后,本次连接不会再有新的报文产生了,我们只需要在TIME-WAIT阶段等待1 MSL时间,就能让网络上所有滞留报文失效。
但是实际情况并不是这样,假如最后的ACK丢失了,服务端会尝试向客户端重传FIN,要求客户端重传ACK。为此,我们需要让TIME-WAIT状态等待更长时间,让服务器重传的报文也失效。具体是要等多久呢?
已知:
- 一个报文从发送到被接收,至多需要1 MSL。
- 客户端在发送最后的ACK后,就不再发送新报文,因此客户端到服务端的滞留报文只需等待1 MSL时间,即可让其失效。
- 服务端接收到最后的ACK,就不再重传FIN,因此,服务端在收到ACK后,只需要等待1 MSL,既可让服务端到客户端的滞留报文失效。
因此可以推断,在确保最后的ACK能到达服务端的前提下。ACK最长传播时间和等待客户端到服务端滞留报文失效时间共为1 MSL。最坏情况下,ACK经历了1 MSL才到达服务端,假如在ACK到达前一瞬间,服务端重传了FIN并发生了滞留,需要等待1 MSL才可让其失效。因此,在最坏情况下,需要2 MSL才可让双向的滞留报文失效。1 MSL+1 MSL= 2 MSL
综上所述,基于原因2,TIME-WAIT时长意义在于。在确保最后的ACK能到达服务端的前提下,等待2 MSL时间是为了确保通信双方的滞留报文都失效。
原因1
原因1在比较官方、专业的书籍中,其实并没有特别明确的说明为什么具体是2 MSL。以下更多是我的个人理解和参考众多博主的总结。
为了可靠地断开连接,服务端希望能接收到最后的ACK,因为这代表着双方完全同意断开连接。而如果最后的ACK丢失,服务端将重传FIN,要求客户端重传最后的ACK。为了达成这一目的,客户端在发送了最后的ACK后,会尝试等待一段时间接收可能发过来的重传FIN。
什么情况下会发生重传FIN呢?服务器发出了最后的FIN后,会进入LAST-ACK状态等待接收最后的ACK。在等待一段时间后服务器发现没有收到最后的ACK,这时服务器会重传最后的FIN。这段等待时间在TCP中被称为RTO( Retransmission Timeout,超时重传时间),是TCP中任何数据包超时重传的等待时间,这个时间是动态计算的。
由MSL的含义可知,报文从发出到被接收至多经历1 MSL时间,因为超过该时间的报文会被丢弃。而发出一个报文到收到确认应答,最坏情况下,显然至多需要2 MSL时间。
思考比较坏的情况,假设服务器觉得报文来回最多需要2 MSL时间,因此设置RTO为2 MSL。服务器在发出最后的FIN就会进入RTO计时,此时如果客户端收到最后的FIN,就会对其应答ACK,然后进入TIME_WAIT状态。
如图所示,假如RTO为2 MSL,显然TIME-WAIT状态也必须至少为2 MSL才可接收到重传的FIN。
疑问:假如重传的FIN经历了很久才到客户端,那客户端还能接收到吗?
上文假设,第一次FIN和重传FIN传播时间相同,因为TIME-WAIT是在接收到FIN后开开始计时,两次FIN传播时间相同的话,TIME-WAIT可以恰好接收到重传的FIN,在图中表示就像一个平行四边形一样。但是假如重传的FIN传播时间比第一次长呢?
如下图所示,显然,客户端是接收不到的。那为什么不将TIME-WAIT设为3 MSL呢?个人理解,是没必要,因为既然第一次FIN的传播时间是x,那我们有理由相信,重传的FIN传播时间也为x。此外,如果重传的FIN也丢失了,那客户端时否要将TIME-WAIT继续无限延长呢?根据维基百科的介绍,可接受的丢包率底线为2.5%,如果发生了重传FIN丢失,说明之前的ACK也丢失了。这是万分之六的概率才会出现的情况。为了如此极端的状况,让大部分连接都等待极长的TIME-WAIT显然是不划算的。
其实这里我们可以推测出,对于原因1,TIME-WAIT的目的是在允许一次丢失ACK的情况下,尽量接收到服务端的重传请求。基于这个原因,我们可以想当然地将TIME-WAIT时长设置为RTO,但是首先,目前的TCP实现中,通信双方的RTO是分别计算的,一方无法直接知道另一方RTO。当然,这是次要问题,因为想要让对方知道自己的RTO,只要在第三次挥手时发过去就行。但是除此之外,RTO只是重传计时器,它并不包含传播时延。并且TCP始终假定网络是不稳定的,因此选取最坏情况下的RTO,即2 MSL作为等待时间是比较保守的选择。
综上所述,基于原因1,TIME-WAIT时长意义在于。在允许一次ACK丢失情况下,尽量保证客户端能接收到服务端的重传请求。
综上所述
综上所述,基于原因1和2,TIME-WAIT时长意义在于。在允许一次ACK丢失情况下,尽量保证主动端能接收到被动端的重传请求。同时,在保证ACK能到达被动端的前提下,保证通信双方所有滞留在网络上的报文失效,不会影响下一次通信。
另外的问题,如果重传的FIN也丢失了怎么办。根据我们上文的结论,TIME-WAIT没有设想过应对这种情况。客户端显然无法知道服务端是否接收到了最终ACK,还是丢失了重传的FIN。客户端会TIME_WAIT结束后关闭连接。如果服务器坚持重传FIN,并且在客户端结束连接后到达客户端,客户端会将此报文视为无效报文并返回RST。服务器收到RST后会将本次连接视为异常断开。
参考
- [1] W.RichardStevens, BillFenner, AndrewM.Rudoff. UNIX网络编程.卷1,套接字联网API.volume 1,The sockets networking API[M]. 人民邮电出版社, 2010:37.
- [2] Why TIME_WAIT state need to be 2MSL long?
- [3] TCP的time wait状态为什么等待2MSL而不是常规的RTO时间呢?
- [4] 为什么TCP4次挥手时等待为2MSL?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。