TCP TIME_WAIT

TCP 中的 ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

因为 Linux 分配给一个用户的文件句柄是有限的,而 TIME_WAIT 和 CLOSE_WAIT 两种状态如果一直被保持,那么意味着对应数目的通道就一直被占着,而且是 “占着茅坑不使劲”,一旦达到句柄数上限,新的请求就无法被处理了,接着就是大量 Too Many Open Files 异常,tomcat崩溃。

如果一直保持在 CLOSE_WAIT 状态,那么只有一种情况,就是在对方关闭连接之后服务器程序自己没有进一步发出 ack 信号。

首先调用 close() 发起主动关闭的一方,在发送最后一个 ACK 之后会进入 time_wait 的状态,也就说该发送方会保持 2MSL 时间之后才会回到初始状态。MSL 值得是数据包在网络中的最大生存时间。

TIME_WAIT产生

time_wait 状态产生的原因:

  1. 为实现 TCP 全双工连接的可靠释放。
  2. 为使旧的数据包在网络因过期而消失。

TIME_WAIT如何避免

首先服务器可以设置 SO_REUSEADDR 套接字选项来通知内核,如果端口忙,但 TCP 连接位于 TIME_WAIT 状态时可以重用端口。

在一个非常有用的场景就是,如果你的服务器程序停止后想立即重启,而新的套接字依旧希望使用同一端口,此时SO_REUSEADDR 选项就可以避免 TIME_WAIT 状态。

发起 socket 主动关闭的一方 socket 将进入 TIME_WAIT 状态,TIME_WAIT 状态将持续 2 个 MSL(Max Segment Lifetime),在 Windows 下默认为 4 分钟,即 240 秒,TIME_WAIT 状态下的 socket 不能被回收使用,具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT 状态的 socket,甚至比处于 Established 状态下的 socket 多的多,严重影响服务器的处理能力,甚至耗尽可用的 socket 停止服务。

TIME_WAIT 是 TCP 协议用以保证被重新分配的 socket 不会受到之前残留的延迟重发报文影响的机制,是必要的逻辑保证。

发现系统存在大量 TIME_WAIT 状态的连接,通过调整内核参数解决:

vi /etc/sysctl.conf

net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将 TIME-WAIT sockets 重新用于新的 TCP 连接,默认为 0,表示关闭。

net.ipv4.tcp_tw_recycle = 1 表示开启 TCP 连接中 TIME-WAIT sockets 的快速回收,默认为 0,表示关闭。

net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间。