TIME_WAIT 过多的解决方法

发布于 2025-03-08 23:18:16 字数 6523 浏览 1 评论 0

上图对排除和定位网络或系统故障时大有帮助,但是怎样牢牢地将这张图刻在脑中呢?那么你就一定要对这张图的每一个状态,及转换的过程有深刻地认识,不能只停留在一知半解之中。下面对这张图的 11 种状态详细解释一下,以便加强记忆!不过在这之前,先回顾一下 TCP 建立连接的三次握手过程,以及关闭连接的四次握手过程。

1、建立连接协议(三次握手)

  1. 客户端发送一个带 SYN 标志的 TCP 报文到服务器。这是三次握手过程中的报文 1。
  2. 服务器端回应客户端的,这是三次握手中的第 2 个报文,这个报文同时带 ACK 标志和 SYN 标志。因此它表示对刚才客户端 SYN 报文的回应;同时又标志 SYN 给客户端,询问客户端是否准备好进行数据通讯。
  3. 客户必须再次回应服务段一个 ACK 报文,这是报文段 3。

2、连接终止协议(四次握手)

由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向的连接。收到一个 FIN 只意味着这一方向上没有数据流动,一个 TCP 连接在收到一个 FIN 后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

  1. TCP 客户端发送一个 FIN,用来关闭客户到服务器的数据传送(报文段 4)。

  2. 服务器收到这个 FIN,它发回一个 ACK,确认序号为收到的序号加 1(报文段 5)。和 SYN 一样,一个 FIN 将占用一个序号。

  3. 服务器关闭客户端的连接,发送一个 FIN 给客户端(报文段 6)。

  4. 客户段发回 ACK 报文确认,并将确认序号设置为收到序号加 1(报文段 7)。

  5. CLOSED:这个没什么好说的了,表示初始状态。

  6. LISTEN:这个也是非常容易理解的一个状态,表示服务器端的某个 SOCKET 处于监听状态,可以接受连接了。

  7. SYN_RCVD:这个状态表示接受到了 SYN 报文,在正常情况下,这个状态是服务器端的 SOCKET 在建立 TCP 连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用 netstat 你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次 TCP 握手过程中最后一个 ACK 报文不予发送。因此这种状态时,当收到客户端的 ACK 报文后,它会进入到 ESTABLISHED 状态。

  8. SYN_SENT:这个状态与 SYN_RCVD 遥想呼应,当客户端 SOCKET 执行 CONNECT 连接时,它首先发送 SYN 报文,因此也随即它会进入到了 SYN_SENT 状态,并等待服务端的发送三次握手中的第 2 个报文。SYN_SENT 状态表示客户端已发送 SYN 报文。

  9. ESTABLISHED:这个容易理解了,表示连接已经建立了。

  10. FIN_WAIT_1:这个状态要好好解释一下,其实 FIN_WAIT_1 和 FIN_WAIT_2 状态的真正含义都是表示等待对方的 FIN 报文。而这两种状态的区别是:FIN_WAIT_1 状态实际上是当 SOCKET 在 ESTABLISHED 状态时,它想主动关闭连接,向对方发送了 FIN 报文,此时该 SOCKET 即进入到 FIN_WAIT_1 状态。而当对方回应 ACK 报文后,则进入到 FIN_WAIT_2 状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应 ACK 报文,所以 FIN_WAIT_1 状态一般是比较难见到的,而 FIN_WAIT_2 状态还有时常常可以用 netstat 看到。

  11. FIN_WAIT_2:上面已经详细解释了这种状态,实际上 FIN_WAIT_2 状态下的 SOCKET,表示半连接,也即有一方要求 close 连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。

  12. TIME_WAIT:表示收到了对方的 FIN 报文,并发送出了 ACK 报文,就等 2MSL 后即可回到 CLOSED 可用状态了。如果 FIN_WAIT_1 状态下,收到了对方同时带 FIN 标志和 ACK 标志的报文时,可以直接进入到 TIME_WAIT 状态,而无须经过 FIN_WAIT_2 状态。

  13. 注:MSL(最大分段生存期) 指明 TCP 报文在 Internet 上最长生存时间,每个具体的 TCP 实现都必须选择一个确定的 MSL 值.RFC 1122 建议是 2 分钟,但 BSD 传统实现采用了 30 秒.TIME_WAIT 状态最大保持时间是 2 * MSL,也就是 1-4 分钟。

  14. CLOSING:这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送 FIN 报文后,按理来说是应该先收到(或同时收到)对方的 ACK 报文,再收到对方的 FIN 报文。但是 CLOSING 状态表示你发送 FIN 报文后,并没有收到对方的 ACK 报文,反而却也收到了对方的 FIN 报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时 close 一个 SOCKET 的话,那么就出现了双方同时发送 FIN 报文的情况,也即会出现 CLOSING 状态,表示双方都正在关闭 SOCKET 连接。

  15. CLOSE_WAIT:这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方 close 一个 SOCKET 后发送 FIN 报文给自己,你系统毫无疑问地会回应一个 ACK 报文给对方,此时则进入到 CLOSE_WAIT 状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以 close 这个 SOCKET,发送 FIN 报文给对方,也即关闭连接。所以你在 CLOSE_WAIT 状态下,需要完成的事情是等待你去关闭连接。

  16. LAST_ACK:这个状态还是比较容易好理解的,它是被动关闭一方在发送 FIN 报文后,最后等待对方的 ACK 报文。当收到 ACK 报文后,也即可以进入到 CLOSED 可用状态了。

最后有 2 个问题的回答,我自己分析后的结论(不一定保证 100%正确)

1、 为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

这是因为服务端的 LISTEN 状态下的 SOCKET 当收到 SYN 报文的建连请求后,它可以把 ACK 和 SYN(ACK 起应答作用,而 SYN 起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的 FIN 报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭 SOCKET,也即你可能还需要发送一些数据给对方之后,再发送 FIN 报文给对方来表示你同意现在可以关闭连接了,所以它这里的 ACK 报文和 FIN 报文多数情况下都是分开发送的。

2、 为什么 TIME_WAIT 状态还需要等 2MSL 后才能返回到 CLOSED 状态?

这是因为:虽然双方都同意关闭连接了,而且握手的 4 个报文也都协调和发送完毕,按理可以直接回到 CLOSED 状态(就好比从 SYN_SEND 状态到 ESTABLISH 状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的 ACK 报文会一定被对方收到,因此对方处于 LAST_ACK 状态下的 SOCKET 可能会因为超时未收到 ACK 报文,而重发 FIN 报文,所以这个 TIME_WAIT 状态的作用就是用来重发可能丢失的 ACK 报文,并保证于此。

查看当前系统下所有连接状态的数:

[root@vps ~]#netstat -n|awk '/^tcp/{++S[$NF]}END{for (key in S) print key,S[key]}'
TIME_WAIT 286
FIN_WAIT1 5
FIN_WAIT2 6
ESTABLISHED 269
SYN_RECV 5
CLOSING 1

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

编辑文件/etc/sysctl.conf,加入以下内容:

net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30

然后执行 /sbin/sysctl -p 让参数生效。

net.ipv4.tcp_syncookies = 1 

表示开启 SYN Cookies。当出现 SYN 等待队列溢出时,启用 cookies 来处理,可防范少量 SYN 攻击,默认为 0,表示关闭;

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 时间

其它参数说明:

net.ipv4.tcp_syncookies = 1 

表示开启 SYN Cookies。当出现 SYN 等待队列溢出时,启用 cookies 来处理,可防范少量 SYN 攻击,默认为 0,表示关闭;

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 = 30 

表示如果套接字由本端要求关闭,这个参数决定了它保持在 FIN-WAIT-2 状态的时间。

net.ipv4.tcp_keepalive_time = 1200 

表示当 keepalive 起用的时候,TCP 发送 keepalive 消息的频度。缺省是 2 小时,改为 20 分钟。

net.ipv4.ip_local_port_range = 1024 65000

表示用于向外连接的端口范围。缺省情况下很小:32768 到 61000,改为 1024 到 65000。

net.ipv4.tcp_max_syn_backlog = 8192 

表示 SYN 队列的长度,默认为 1024,加大队列长度为 8192,可以容纳更多等待连接的网络连接数。

net.ipv4.tcp_max_tw_buckets = 5000 

表示系统同时保持 TIME_WAIT 套接字的最大数量,如果超过这个数字,TIME_WAIT 套接字将立刻被清除并打印警告信息。默认为 180000,改为 5000。对于 Apache、Nginx 等服务器,上几行的参数可以很好地减少 TIME_WAIT 套接字数量,但是对于 Squid,效果却不大。此项参数可以控制 TIME_WAIT 套接字的最大数量,避免 Squid 服务器被大量的 TIME_WAIT 套接字拖死。

注:

net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1

设置这两个参数:

  • reuse 是表示是否允许重新应用处于 TIME-WAIT 状态的 socket 用于新的 TCP 连接
  • recyse 是加速 TIME-WAIT sockets 回收

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

上一篇:

下一篇:

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

无声情话

暂无简介

文章
评论
26 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文