为什么 TCP 中的延迟段预防(TIME_WAIT)只对一侧进行?

网络工程 通讯协议 协议理论 传输协议 第4层
2021-07-10 00:54:04

TIME_WAIT 的原因之一是为了防止旧数据包/重复项迟到,从而破坏同一(src ip、src 端口、dst ip、dst 端口)四元组上的后续连接。

这是有道理的,但我不明白为什么它只在关闭连接的一侧完成。

让我们假设两个连接都发送和接收了 FIN 和 ACK - 他们在旧的重复项进来时不是在同一条船上吗?那么为什么另一方不也用 TIME_WAIT 状态来保护自己呢?

2个回答

我不明白为什么它只在关闭连接的一侧完成。

请记住,当第一方发送 FIN 时,它已完成发送,但只要另一方愿意发送,它仍然会接收和处理,并为收到的任何段发送 ACK,直到另一方完成。它进入 FIN-WAIT-1 状态,直到它收到它的 FIN 的 ACK,并在它收到它的 FIN 的 ACK 后进入 FIN-WAIT-2 状态。它不能强制对方关闭(除了RST),它会接收和处理,直到连接关闭。

当第二方完成发送后,它会发送一个 FIN 使第一方发送一个 ACK​​,然后进入 TIME-WAIT (2MSL) 以确保第二方收到它的 ACK。第二方收到ACK后,还要等待2MSL。在这些超时之后,每一方都关闭连接。这实际上是对称的,因为双方在关闭连接之前等待 2MSL。

一旦连接关闭,并且任何一方收到任何段之后,都需要 RST 作为回复。第二端的超时是为了确保没有到达会导致 RST 的无序段。

它在RFC 793,传输控制协议中进行了解释,它是 TCP 的定义。第一个 FIN 的发起者进入 TIME-WAIT 因为“ TIME-WAIT - 表示等待足够的时间过去以确保远程 TCP 收到其连接终止请求的确认。 ” 在最后一个 FIN 的最后一个 ACK​​ 之后,最后一个 FIN 的发送者必须等待“ Timeout=2MSL ”

状态在第 21 和 22 页解释,连接状态图在第 23 页。

防止影响未来连接丢失和延迟的数据包只是一个次要目的TIME_WAIT1TCP 凭借接收窗口以及在现代实现中的时间戳对此类数据包进行了强化。

相反,TIME_WAIT的主要目的是处理 finalACK丢失的情况。在这种情况下,决赛FIN将被重新接收,决赛ACK将需要重新传输。这是必要的,因为没有ACKing an ACK,否则两个端点将ACK永远互相使用。取而代之的是,一侧发送最终的ACK并等待一段时间,TIME_WAIT以便相当确定另一端点收到了ACKFIN.

然而,被动关闭的端点并没有进入TIME_WAIT我认为是 TCP 设计缺陷的状态看看这个页面的TCP状态图,考虑倒数第二个 ACK丢失的情况:

在此处输入图片说明

图表关键字:绿色:状态 | 蓝色:API 调用 | 红色:错误、事件和注释 | 紫线:连接状态保存点

问题是,如果倒数第二个ACK端点丢失,两个端点会不同步:端点 A 认为正在同时关闭并等待 final ACK,而端点 B 认为连接已完全关闭(图 1)。

如果连接保持原样(图 2),这最终会自行清理,但没有什么能阻止端点 B 尝试重新打开连接(图 3),这会导致connection reset错误(图 4 和图 5)。

更好的设计是合并CLOSINGLAST_ACK状态,以便两端都通过TIME_WAIT,代价是被动关闭端的内存使用量略有增加。


1TIME_WAITRFC 739 中没有直接提到防止来自连接的旧版本的段干扰新版本的次要目的,并且只能从安静时间部分和RFC 4.2.2.13 部分中模糊地推断出来1122 . 我注意到2012 年的RFC 6528 第 2 节指出:

有趣的是,事实上,通过防止在 2*MSL 之前创建先前连接的新化身,自对应于旧的化身最后一次出现(其中“MSL”是“最大段生命周期”[RFC0793])。这是通过 TIME-WAIT 状态和 TCP 的“安静时间”概念实现的

但是我发现这是不正确的,因为重置的连接不会进入TIME_WAIT状态,而是进入CLOSEDLISTEN状态。因此,当连接可以重新打开时,旧的段可能仍在飞行中。这可能可以通过重置连接进入类似TIME_WAIT状态来补救对这种双重目的的混淆可能是这些不一致的根本原因。