经典问题:TIME-WAIT 时长为何为2 MSL?

这是个很经典的问题,TCP断开连接时的四次挥手中,客户端在发送最后的ACK后要进入TIME-WAIT状态,这个状态时长为2倍MSL。

MSL是Maximum Segment Lifetime的英文缩写,可译为“最长报文寿命”,它是任何报文在网络上存在的最长的最长时间,超过这个时间报文将被丢弃。

TIME-WAIT时长为2 MSL这主要有两个原因

  1. 尽量让服务端收到最后的ACK
  2. 确保当前连接的报文不会出现在下一次连接中

参考《Unix 网络编程 卷1(第三版)》,专业一点的说法是

  1. 可靠地实现TCP全双工连接的终止
  2. 允许老的重复分节在网络中消逝

以下段落假定在一次四次挥手中,主动方为客户端,被动方为服务端。

分析

原因2

我们先说原因2。为什么要先说原因2?因为原因2更容易理解,也更明确。我们知道TCP是面向字节流、面向连接的。但是假如你对计算机网络课程前几章的有记忆,你就知道,TCP之下的网络层并不使用电路交换,而是使用分组交换。换言之,即使TCP是面向连接的,底层实现仍然是一个个分散的数据包、报文。

TCP大致通过一个四元组来标记一条连接,分别是:源IP、目的IP、源端口、目的端口。假设在一次连接中,部分报文因为某种原因滞留在路由网络中,然后这次连接断开了,再然后客户复用了端口,开启了和当前服务器一次新的连接。此时可以看见,因为客户端复用了端口,而且客户端和服务器短期内IP都不会变,而且服务器通常只会又有一个端口监听。因此当前连接的四元组和上次连接一样。此时假如上一次连接中滞留的报文又恢复传播,那就会进入当前连接中,因为这些报文本不属于本次连接,这可能造成数据混乱。实际上由于序号机制的存在,这种情况发生几率较小,但是显然还是有必要避免。

参考TCP四次挥手图。已知每个报文最多只有1 MSL寿命,因此,我们可以想当然的猜想,客户端发送了最后的ACK后,本次连接不会再有新的报文产生了,我们只需要在TIME-WAIT阶段等待1 MSL时间,就能让网络上所有滞留报文失效。

经典问题:TIME-WAIT 时长为何为2 MSL?_第1张图片

但是实际情况并不是这样,假如最后的ACK丢失了,服务端会尝试向客户端重传FIN,要求客户端重传ACK。为此,我们需要让TIME-WAIT状态等待更长时间,让服务器重传的报文也失效。具体是要等多久呢?
已知:

  1. 一个报文从发送到被接收,至多需要1 MSL。
  2. 客户端在发送最后的ACK后,就不再发送新报文,因此客户端到服务端的滞留报文只需等待1 MSL时间,即可让其失效。
  3. 服务端接收到最后的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。

经典问题:TIME-WAIT 时长为何为2 MSL?_第2张图片

疑问:假如重传的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显然是不划算的。

经典问题:TIME-WAIT 时长为何为2 MSL?_第3张图片

其实这里我们可以推测出,对于原因1,TIME-WAIT的目的是在允许一次丢失ACK的情况下,尽量接收到服务端的重传请求。基于这个原因,我们可以想当然地将TIME-WAIT时长设置为RTO,但是首先,目前的TCP实现中,通信双方的RTO是分别计算的,一方无法直接知道另一方RTO。当然,这是次要问题,因为想要让对方知道自己的RTO,只要在第三次挥手时发过去就行。更重要的是问题是,RTO只是重传计时器,它并不能代表从重传开始到报文被接收的时间。而TCP假定网络是不稳定的,因此选取最坏情况的2 MSL时长RTO作为等待时间。

综上所述,基于原因1,TIME-WAIT时长意义在于。在允许一次ACK丢失情况下,尽量保证客户端能接收到服务端的重传请求。

综上所述

综上所述,基于原因1和2,TIME-WAIT时长意义在于。在允许一次ACK丢失情况下,尽量保证主动端能接收到被动端的重传请求。同时,在保证ACK能到达被动端的前提下,保证通信双方所有滞留在网络上的报文失效,不会影响下一次通信。

另外的问题,如果重传的FIN也丢失了怎么办。根据我们上文的结论,TIME-WAIT没有设想过应对这种情况。客户端显然无法知道服务端是否接收到了最终ACK,还是丢失了重传的FIN。客户端会TIME_WAIT结束后关闭连接。如果服务器坚持重传FIN,并且在客户端结束连接后到达客户端,客户端会将此报文视为无效报文并返回RST。服务器收到RST后会将本次连接视为异常断开。

参考

你可能感兴趣的:(经典问题:TIME-WAIT 时长为何为2 MSL?)