https://www.zhihu.com/question/67013338
问题
B收到ACK,关闭连接。但是A无法知道ACK是否已经到达B,于是开始等待?等待什么呢?假如ACK没有到达B,B会为FIN这个消息超时重传 timeout retransmit ,那如果A等待时间足够,又收到FIN消息,说明ACK没有到达B,于是再发送ACK,直到在足够的时间内没有收到FIN,说明ACK成功到达。这个等待时间至少是:B的timeout + FIN的传输时间
那么B的timeout时间不应该是FIN传送时间的最大值MSL+ACK传送时间的最大值MSL=2MSL吗?
这样一来整个等待时间不应该是3MSL吗?
回答1
主动断开的一侧为A,被动断开的一侧为B。
第一个消息:A发FIN
第二个消息:B回复ACK
第三个消息:B发出FIN
此时此刻:B单方面认为自己与A达成了共识,即双方都同意关闭连接。此时,B能释放这个TCP连接占用的内存资源吗?不能,B一定要确保A收到自己的ACK、FIN。所以B需要静静地等待A的第四个消息的到来
第四个消息:A发出ACK,用于确认收到B的FIN当B接收到此消息,即认为双方达成了同步:双方都知道连接可以释放了,此时B可以安全地释放此TCP连接所占用的内存资源、端口号。所以被动关闭的B无需任何wait time,直接释放资源。
但,A并不知道B是否接到自己的ACK,A是这么想的:
-
1)如果B没有收到自己的ACK,会超时重传FiN那么A再次接到重传的FIN,会再次发送ACK
-
2)如果B收到自己的ACK,也不会再发任何消息,包括ACK无论是1还是2,A都需要等待,要取这两种情况等待时间的最大值,以应对最坏的情况发生
这个最坏情况是:去向ACK消息最大存活时间(MSL) + 来向FIN消息的最大存活时间(MSL)。这恰恰就是2MSL( Maximum Segment Life)。
等待2MSL时间,A就可以放心地释放TCP占用的资源、端口号,此时可以使用该端口号连接任何服务器。
为何一定要等2MSL?
如果不等,释放的端口可能会重连刚断开的服务器端口,这样依然存活在网络里的老的TCP报文可能与新TCP连接报文冲突,造成数据冲突,为避免此种情况,需要耐心等待网络老的TCP连接的活跃报文全部死翘翘,2MSL时间可以满足这个需求(尽管非常保守)!
回答2
根据第三版《UNIX 网络编程 卷 1》2.7 节,TIME_WAIT 状态的主要目的有两个:
-
优雅的关闭 TCP 连接,也就是尽量保证被动关闭的一端收到它自己发出去的 FIN 报文的 ACK 确认报文;
-
处理延迟的重复报文,这主要是为了避免前后两个使用相同四元组的连接中的前一个连接的报文干扰后一个连接。
注:很多博文只讨论了第一点,并且轻易的给出了错误的理由:最坏情况,ACK 报文的发送和 FIN 报文的重传各自需要 1MSL,因此是 2MSL。仔细想想之后,假设 A 是主动关闭的一方,B 是被动关闭。假如现在 A 收到 FIN 之后,为了实现目标 1,即保证 B 能够收到自己的 ACK 报文。那么 A 完美的等待时间不是 2MSL,而应该是从 B 发送第一个 FIN 报文开始计时到它最后一次重传 FIN 报文这段时长加上 MSL。但这个计算方式过于保守,只有在所有的 ACK 报文都丢失的情况下才需要这么长的时间;
另外,第一个目标虽然重要,但并不十分关键,因为既然已经到了关闭连接的最后一步,说明在这个 TCP 连接上的所有用户数据已经完成可靠传输,所以要不要完美的关闭这个连接其实已经不是那么关键了。因此,可能RFC 标准的制定者才决定以网络丢包不太严重为前提条件,然后根据第二个目标来计算 TIME_WAIT 状态应该持续的时长。
等待 2MSL 的真正目的是为了避免前后两个使用相同四元组的连接中的前一个连接的报文干扰后一个连接,换句话说,就是为了让此次 TCP 连接中的所有报文在网络中消失。
假如现在 A 发送 ACK 后,最坏情况下,这个 ACK 在 1MSL 时到达 B;此时 B 在收到这个 ACK 的前一刹那,一直在重传 FIN,这个 FIN 最坏会在 1MSL 时间内消失。因此从 A 发送 ACK 的那一刹那开始,等待 2MSL 可以保证 A 发送的最后一个 ACK,和 B 发送的最后一个 FIN 都在网络中消失。