TCP TIME_WAIT 状态

四次挥手

当需要断开一个 TCP 连接时,需要四次挥手,每一次需要发送一报文,同时 TCP 连接会进行状态的变化。在挥手时的最后一个 ACK 发出后,TCP 连接会进入 TIME_WAIT 状态。

TIME_WAIT 状态通常需要等待 2MSL 的时间再变成 CLOSED 状态。而因为这个状态,经常会产生一些奇怪的问题。本文整理了 TIME_WAIT 状态的作用和如何应对大量 TIME_WAIT 的建议。

TIME_WAIT 的作用

TIME_WAIT 主要有两个作用:

  1. 防止延迟的数据段被其他使用相同源地址、源端口、目的地址以及目的端口的 TCP 连接收到
  2. 保证远程 TCP 连接被正确关闭

旧报文干扰新连接

因为数据段的网络传输时间不确定,所以可能会收到上一次 TCP 连接中未被收到的数据段。

在经过 2MSL 时间,就可以使本连接持续的时间内所产生的所有报文都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文。

保证远程 TCP 连接被正确关闭

1. 让 TCP 连接等待被动关闭连接的一方收到 FIN 对应的 ACK 消息。

因为客户端发出的 ACK 可能还没有被服务端接收,服务端可能还处于 LAST_ACK 状态。服务端因为没有收到 ACK 消息,所以仍然认为当前连接是合法的。

新连接的建立时会回复 RST 消息终止连接,客户端重新发送 SYN 消息请求握手时会收到服务端的 RST 消息,连接建立的过程就会被终止。

2. 等待足够长的时间以确定远程的 TCP 连接接收到了其发出的终止连接消息 FIN 对应的 ACK

2MSL = ACK 到达服务器 + 服务器发送 FIN 重传包,一来一回。

  • 服务端正常收到了 ACK 消息并关闭当前 TCP 连接,
  • 服务端没有收到 ACK 消息,重新发送 FIN 关闭连接并等待新的 ACK 消息

大量 TIME_WAIT 如何处理

加机器

TCP 的 TIME_WAIT 状态有着非常重要的作用,它是保证 TCP 协议可靠性不可缺失的设计。

如果能通过加机器解决的话就尽量加机器。如果不能解决的话,我们就需要理解其背后的设计原理并尽可能避免修改默认的配置。

调整短连接为长连接

TIME_WAIT 是在关闭连接时出现,通过使用长连接减少连接关闭,进而减少 TIME_WAIT 状态的出现。

调整内核参数

  1. net.ipv4.tcp_max_tw_buckets:表示系统同时保持 TIME_WAIT 套接字的最大数量。超过此数量时,系统会立即清理出多余的 TIME_WAIT 连接。意味着有些连接并没有成功等待 2MSL,就会造成通讯异常。一般不建议调整。
  2. net.ipv4.tcp_tw_recycle:开启 TCP 连接中 TIME-WAIT sockets 的快速回收,默认为 0,表示关闭。
    • 已经在 Linux 4.12 中移除
    • 如果 NAT 或 LVS 做负载均衡出现的连接失败率高
      • 从后端服务器的角度看,原本不同客户端的请求经过 LVS 的转发,就可能会被认为是同一个连接。
      • 具体的表现通常是是客户端明明发送的 SYN,但服务端就是不响应 ACK。
  3. net.ipv4.tcp_fin_timeout:修改系统默认的 TIMEOUT 时间
  4. net.ipv4.tcp_tw_reuse:(推荐)开启重用。允许将 TIME-WAIT sockets 重新用于新的 TCP 连接,默认为 0,表示关闭。
    • 开启重用机制需要依赖 tcp_timestamps 的功能
    • 开启重用相比开启快速回收要更安全一点
  5. net.ipv4.ip_local_port_range:(推荐)表示用于向外连接的端口范围。调整可用端口范围,增加可同时存在的 TCP 连接数上限。

调整连接参数(推荐

使用 SO_LINGER 选项并设置暂存时间 l_linger 为 0。当关闭 TCP 连接时,内核就会直接丢弃缓冲区中的全部数据并向服务端发送 RST 消息直接终止当前的连接。

引用

  1. 为什么 TCP 协议有 TIME_WAIT 状态
  2. TCP 选项 SO_LINGER
作者

Jakes Lee

发布于

2021-02-22

更新于

2021-11-22

许可协议

评论