服务器 time_wait 和 close_wait 详解

有的时候 netstat 查看会看到非常多的 tcp 连接状态为 time wait 或者 close wait。这两个问题都不算难处理,这里分别解释一下两个问题出现的原理。

tcp 挥手状态装换

首先理一下 tcp 挥手时的状态转换。
假设一个 tcp 连接,一端为 A,一端为 B。

  • A 向 B 发送释放连接报文,之后 A 进入 fin-wait-1 状态。
  • B 收到 A 的报文后,发送 ack 确认报文,这时候 B 进入 close-wait 状态。
  • A 收到 B 的 ack 之后进入 fin-wait-2 状态。

  • B 发送数据完毕,向 A 发送释放连接报文,这时候 B 进入 last-ack 状态。

  • A 收到 B 发送的释放报文后,进入 time-wat 状态。回复给 B ack,然后等待 2MSL 后进入 close 状态。B 收到 A 的 ack 后进入 close 状态。

time wait

从上面来看,time wait 的时候其实 tcp 挥手过程已经基本完成了,只是主动关闭的一方需要等待 2MSL 才进入 close 状态。这个等待的过程就是 time_wait 状态。这个时候已经没有被动关闭一方的事情了。出现大量的 time wait 说明有大量的 tcp 连接主动关闭的一方处在 2MSL 中,即有大量 tcp 连接关闭。

所以出现这类问题一般的情况是频繁的创建了 tcp 连接,然后又频繁的关闭。

解决方案:是否能复用 tcp 连接;使用连接池。

close wait

close wait 是被动关闭的一方收到了另一方发起的释放请求,但是自己迟迟没有结束发送数据,向对方发送释放连接报文。

出现大量的 close wait 说明有大量 tcp 连接被关闭,而被动却一方没有结束。

这种情况出现场景大部分是 tcp 连接被强制关闭。比如数据库连接,一般都要求无论处理是否成功,最后一定要关掉连接。如果漏掉了关闭连接这一步,这个连接就不会被主动关闭,然后等到数据库系统认为连接超时就会强制关闭,这是我们的程序接收到释放链接报文,但是我们的程序不认为自己已经发送完了数据(因为该关闭的地方我们没有写,就认为不该关闭),就会迟迟不发送释放连接的报文,最终就卡在了 close wait。

解决方案:close wait 问题一般在自己的程序里,需要自己去检查问题。另外 close wait 会占用连接,这样最终会耗尽服务器资源,产生 Too Many Open Files 问题,所以需要立即解决。

为什么其他状态的 tcp 连接不多

其实 tcp 连接各个情况下都会产生大量某状态的数据,只是由于操作系统的限制,某一个状态的连接过多之后就会被强制回收。但是 2MSL 是系统允许的,所以这段时间内不会被回收,同样 close wait 系统允许存活的时间是 2 小时左右,所以才会大量存在。

参考:
https://www.cnblogs.com/sunxucool/p/3449068.html
谈谈 TCP 的 TIME_WAIT