LLLeon / Blog

LLLeon 的部落格
15 stars 4 forks source link

TCP 断开连接时 TIME_WAIT 存在的原因 #17

Open LLLeon opened 5 years ago

LLLeon commented 5 years ago

TIME_WAIT 是主动关闭 TCP 连接一方在发出最后一个 ACK 后需要等待的状态,时长为 2MSL。TIME_WAIT 状态的存在主要有以下原因。(因为 TCP 连接的断开一般是由客户端发起关闭连接的操作,所以为了方便描述,后文把主动关闭方称为 C 端(Client),被动关闭方称为 S 端(Server)。)

1. C 端要确认 S 端是否有收到此 ACK

因为 TCP 连接是全双工的,因此断开连接需要双方连接都关闭。整个断开连接的过程简要描述如下:

  1. 过程一:C 端发送 FIN,S 端收到后发送该 FIN 的 ACK;
  2. 过程二:S 端发送 FIN,C 端收到后发送该 FIN 的 ACK;
  3. 过程三:S 端收到 ACK。

要实现 TCP 全双工连接的正常终止,必须处理终止过程中四个报文中任何一个报文的丢失情况 。完成以上三个过程,才说明 TCP 全双工连接已经断开。

在过程三后,S 端就可以 100% 确认连接已经可以关闭,因此它便可以直接进入 CLOSED 状态了。然而对于 C 端来说,它无法确定最后发给 S 端的那个 ACK 是否已经被收到。根据 TCP 协议规范,不对 ACK 进行 ACK,因此它不可能再收到 S 端的 ACK 了。

那么在这里就陷入了僵局,TCP 连接的主动关闭方如何来保证整个断开连接过程的闭合?这时协议外的东西就起作用了。

TCP 报文段有一个超时值,即 MSL(Maximum Segment Lifetime),它是 TCP 报文段在网络上存在的最长时间,超过这个时间报文将被丢弃。MSL 在 RFC 1122 上建议是 2 分钟,而源自 berkeley 的 TCP 实现传统上使用 30 秒。TIME_WAIT 状态维持时间是 2MSL 时间长度,也就是在 1-4 分钟。

这类超时值非常重要,因为它们给出了一个物理意义上的不可逾越的界限,它们是自洽协议的唯一外部输入。

综上,如果这个 ACK 丢失,S 端将重发出最终的 FIN,因此 C 端必须要维持 TIME_WAIT 状态,以允许它重发最终的 ACK。如果 C 端不维持 TIME_WAIT 状态,而是在发出最终的 ACK 后就转为 CLOSED 状态,那么 C 端将响应 RST 报文,S 端收到后将此解释成一个错误(RST 表示复位,用来异常的关闭连接)。

2. TIME_WAIT 的时间为什么是 2MSL

等待 2MSL 时间主要目的是怕 S 端没收到最后一个 ACK,那么 S 端将在超时后重发第三次握手的 FIN 包,C 端接到重发的 FIN 包后可以再发一个 ACK 应答包。

分析 TIME_WAIT 时长是 2MSL 的原因:

3. 处于 TIME_WAIT 状态的连接为什么不能启动一个新连接

TCP 报文可能由于路由器异常而“迷途”,在迷途期间,TCP 发送端可能因确认超时而重发这个报文,迷途的报文在路由器修复后也会被送到最终目的地,这个迟到的迷途报文到达时可能会引起问题。

在关闭前一个连接之后,马上又重新建立起一个相同的 IP 和端口之间的新连接,前一个连接的迷途重复分组在前一个连接终止后到达,而被新连接收到了。

为了避免这个情况,在 TIME_WAIT 状态下,上一次建立连接的套接字 (Socket) 将不可再重新启用,也就是同一个网卡 / IP 不可再建立同样端口号的连接,如果再重新创建系统将会报错。

要等待 TIME_WAIT 这个时间,也是为了避免有些报文段在网络上滞留,被对方收到的时候如果刚好又启用了一个完全一样的套接字,那么就会被认为是这个新连接的数据。因此为了让所有 “迷路” 的报文彻底消失后,才能启用相同的套接字。

4. 参考

  1. http://elf8848.iteye.com/blog/1739571
  2. https://en.wikipedia.org/wiki/Maximum_segment_lifetime