计算机网络面经八股-为什么客户端的 TIME-WAIT 状态必须等待 2MSL ?

主要有两个原因:

  1. 确保 ACK 报文能够到达服务端,从而使服务端正常关闭连接。
    第四次挥手时,客户端第四次挥手的 ACK 报文不一定会到达服务端。服务端会超时重传 FIN/ACK 报文,此时如果客户端已经断开了连接,那么就无法响应服务端的二次请求,这样服务端迟迟收不到 FIN/ACK 报文的确认,就无法正常断开连接。
    MSL 是报文段在网络上存活的最长时间。客户端等待 2MSL 时间,即「客户端 ACK 报文 1MSL 超时 + 服务端 FIN 报文 1MSL 传输」,就能够收到服务端重传的 FIN/ACK 报文,然后客户端重传一次 ACK 报文,并重新启动 2MSL 计时器。如此保证服务端能够正常关闭。
    如果服务端重发的 FIN 没有成功地在 2MSL 时间里传给客户端,服务端则会继续超时重试直到断开连接。
  2. 防止已失效的连接请求报文段出现在之后的连接中。
    TCP 要求在 2MSL 内不使用相同的序列号。客户端在发送完最后一个 ACK 报文段后,再经过时间 2MSL,就可以保证本连接持续的时间内产生的所有报文段都从网络中消失。这样就可以使下一个连接中不会出现这种旧的连接请求报文段。或者即使收到这些过时的报文,也可以不处理它。

原因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. 一个报文从发送到被接收,至多需要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。

疑问:假如重传的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后会将本次连接视为异常断开。

  • 36
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值