为何tcp的TIME_WAIT状态要维持2MSL

本文主要分析为何TIME_WAIT状态的持续时间是2MSL而不是1MSL,3MSL或其它的时长,而不会详细描述为何须要TIME_WAIT状态。linux

阅读本文须要的预备知识:编程

  • 了解TCP协议的状态变迁;网络

  • 了解TCP拆链的四次挥手过程;tcp

  • 了解为何须要TIME_WAIT状态。函数

正文this

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

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

  • 优雅的关闭TCP链接,也就是尽可能保证被动关闭的一端收到它本身发出去的FIN报文的ACK确认报文;ci

  • 处理延迟的重复报文,这主要是为了不先后两个使用相同四元组的链接中的前一个链接的报文干扰后一个链接。开发

很明显,要实现上述两个目标,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个目的,但这段时间具体应该多长主要是为了达成上述第二个目的而设计的。

相关文章
相关标签/搜索