详述 TCP 的 TIME_WAIT 状态要维持 2MSL 的原因

2022-09-08 16:23:45 浏览数 (1)

文章目录

  • 前言
  • 正文

前言

本文主要分析为什么 TIME_WAIT 状态的持续时间是 2MSL 而不是 1MSL,3MSL 或其它的时长,而不会详细描述为什么需要 TIME_WAIT 状态。阅读本文需要的预备知识:

  • 了解 TCP 协议的状态变迁;
  • 了解 TCP 拆链的四次挥手过程;
  • 了解为什么需要 TIME_WAIT 状态。

正文

其实这个问题在《TCP/IP详解》以及《UNIX网络编程》这两本书中都有提及,但这两本书上的描述都比较简洁并不是特别容易理解,记得在第一次看《UNIX网络编程》时,我曾经反复阅读相关段落并花了不少时间来想这个问题,但并没有搞得很清楚,始终是懂非懂的样子,直至后来有机会参与 TCP/IP 协议栈的开发后才真正 get 到这个问题的关键点。

根据第三版《UNIX网络编程 卷1》2.7 节,TIME_WAIT 状态的主要目的有两个:

  • 优雅的关闭 TCP 连接,也就是尽量保证被动关闭的一端收到它自己发出去的 FIN 报文的 ACK 确认报文
  • 处理延迟的重复报文,这主要是为了避免前后两个使用相同四元组的连接中的前一个连接的报文干扰后一个连接

很明显,要实现上述两个目标,TIME_WAIT 状态需要持续一段时间,但这段时间应该是多长呢?

如果只考虑上述第一个目标,则 TIME_WAIT 状态需要持续的时间应该参考对端的 RTO(重传超时时间)以及 MSL(报文在网络中的最大生存时间)来计算而不是仅仅按 MSL 来计算,因为只要对端没有收到针对 FIN 报文的 ACK,就会一直持续重传 FIN 报文直到重传超时,所以最能实现完美关闭连接的时长计算方式应该是从对端发送第一个 FIN 报文开始计时到它最后一次重传 FIN 报文这段时长加上 MSL,但这个计算方式过于保守,只有在所有的 ACK 报文都丢失的情况下才需要这么长的时间;另外,第一个目标虽然重要,但并不十分关键,因为既然已经到了关闭连接的最后一步,说明在这个 TCP 连接上的所有用户数据已经完成可靠传输,所以要不要完美的关闭这个连接其实已经不是那么关键了。因此,(我猜)RFC 标准的制定者才决定以网络丢包不太严重为前提条件,然后根据第二个目标来计算 TIME_WAIT 状态应该持续的时长。

再来看一下《UNIX网络编程》在描述为什么需要 TIME_WAIT 状态时的一段话:

Since the duration of the TIME_WAIT state is twice the MSL, this allows MSL seconds for packet in one direction to be lost, and another MSL seconds for the reply to be lost. By enforcing this rule, we are guaranteed that when we successfully establish a TCP connecton, all old duplicates from previous incarnations of the connection have expired in the network.

这段文字说明了 TIME_WAIT 状态持续 2MSL 的时间可以让一个 TCP 连接的两端发出的报文都从网络中消失,从而保证下一个使用了相同四元组的 TCP 连接不会被上一个连接的报文所干扰。

如何理解 TIME_WAIT 状态持续 2MSL 的时间就可以让一个 TCP连 接的两端发出的报文都从网络中消失呢?

首先我们需要了解如下要点:

  1. TCP 连接中的一端发送了 FIN 报文之后如果收不到对端针对该 FIN 的 ACK,则会反复多次重传 FIN 报文,大约持续几分钟;
  2. 被动关闭处于 LAST_ACK 状态的一端在收到最后一个 ACK 之后不会发送任何报文,立即进入 CLOSED 状态;
  3. 主动关闭的一端在收到被动关闭端发送过来的 FIN 报文并回复 ACK 之后进入 TIME_WAIT 状态;
  4. 之所以 TIME_WAIT 状态需要维持一段时间而不是进入 CLOSED 状态,是因为需要处理对端可能重传的 FIN 报文或其它一些因网络原因而延迟的数据报文,不处理这些报文可能导致前后两个使用相同四元组的连接中的后一个连接出现异常(详见《UNIX网络编程》卷 1 的 2.7 节 第三版);
  5. 处于 TIME_WAIT 状态的一端在收到重传的 FIN 时会重新计时(rfc793以及 Linux Kernel 源代码tcp_timewait_state_process函数)。

下面我们开始分析为什么在发送了最后一个 ACK 报文之后需要等待 2MSL 时长来确保没有任何属于当前连接的报文还存活于网络之中(前提是在这 2MSL 时间内不再收到对方的 FIN 报文,但即使收到了对端的 FIN 报文也并不影响我们的讨论,因为如果收到 FIN 则会回复 ACK 并重新计时)。

为了便于描述,我们设想有一个处于拆链过程中的 TCP 连接,这个连接的两端分别是 A 和 B,其中 A 是主动关闭连接的一端,因为刚刚向对端发送了针对对端发送过来的 FIN 报文的 ACK,此时正处于 TIME_WAIT 状态;而 B 是被动关闭的一端,此时正处于 LAST_ACK 状态,在收到最后一个 ACK 之前它会一直重传 FIN 报文直至超时。随着时间的流逝,A 发送给 B 的 ACK 报文将会有两种结局:

  1. ACK 报文在网络中丢失;如前所述,这种情况我们不需要考虑,因为除非多次重传失败,否则 AB 两端的状态不会发生变化直至某一个 ACK 不再丢失。
  2. ACK 报文被 B 接收到。我们假设 A 发送了 ACK 报文后过了一段时间t之后 B 才收到该 ACK,则有0 < t <= MSL。因为 A 并不知道它发送出去的 ACK 要多久对方才能收到,所以 A 至少要维持 MSL 时长的 TIME_WAIT 状态才能保证它的 ACK 从网络中消失。同时处于 LAST_ACK 状态的 B 因为收到了 ACK,所以它直接就进入了 CLOSED 状态,而不会向网络发送任何报文。所以晃眼一看,A 只需要等待 1 个 MSL 就够了,但仔细想一下其实 1 个 MSL 是不行的,因为在 B 收到 ACK 前的一刹那,B 可能因为没收到 ACK 而重传了一个 FIN 报文,这个 FIN 报文要从网络中消失最多还需要一个 MSL 时长,所以 A 还需要多等一个 MSL。

综上所述,TIME_WAIT 至少需要持续 2MSL 时长,这 2 个 MSL 中的第一个 MSL 是为了等自己发出去的最后一个 ACK 从网络中消失,而第二 MSL 是为了等在对端收到 ACK 之前的一刹那可能重传的 FIN 报文从网络中消失。另外,虽然说维持 TIME_WAIT 状态一段时间有 2 个目的,但这段时间具体应该多长主要是为了达成上述第二个目的而设计的。


  • 原文链接:https://www.cnblogs.com/abozhang/p/10974627.html

0 人点赞