解决netstat TIME_WAIT状态过多问题(转)

在实际生产过程中,遇到过tcp连接有十几万个TIME_WAIT的连接,通过设置相关参数解决,这里直接引用这个连接。

 

转自:https://www.cnblogs.com/even160941/p/15180216.html

通过命令查看所有状态的个数:

netstat -antlp|awk '/tcp/ {print $6}'|sort|uniq -c
     16 CLOSING
    130 ESTABLISHED
    298 FIN_WAIT1
     13 FIN_WAIT2
      9 LAST_ACK
      7 LISTEN
    103 SYN_RECV
   5204 TIME_WAIT
各个状态的含义解释:

状态	描述
CLOSED	无连接是活动的或正在进行
LISTEN	服务器在等待进入呼叫
SYN_RECV	一个连接请求已经到达,等待确认
SYN_SENT	应用已经开始,打开一个连接
ESTABLISHED	正常数据传输状态
FIN_WAIT1	应用说它已经完成
FIN_WAIT2	另一边已同意释放
ITMED_WAIT	等待所有分组死掉
CLOSING	两边同时尝试关闭
TIME_WAIT	另一边已初始化一个释放
LAST_ACK	等待所有分组死掉
如发现系统存在大量TIME_WAIT状态的连接,通过调整内核参数解决,编辑内核文件,加入以下内容:

vim /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 时间。
下面附上TIME_WAIT状态的意义:

客户端与服务器端建立TCP/IP连接后关闭SOCKET后,服务器端连接的端口状态为TIME_WAIT,是不是所有执行主动关闭的socket都会进入TIME_WAIT状态呢?有没有什么情况使主动关闭的socket直接进入CLOSED状态呢?主动关闭的一方在发送最后一个 ack 后就会进入 TIME_WAIT 状态 停留2MSL(max segment lifetime报文最大生存时间)时间这个是TCP/IP必不可少的,也就是"解决"不了的。也就是TCP/IP设计者本来是这么设计的。

主要有两个原因:

1.防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)。
2.可靠的关闭TCP连接。
在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED。TIME_WAIT 并不会占用很大资源的,除非受到攻击。还有,如果一方 send 或 recv 超时,就会直接进入 CLOSED 状态。

这样修改完内核参数完,再去netstat查看网络连接,发现time_wait状态的连接基本都消失了。

netstat -antlp|awk '/tcp/{print $6}'|sort|uniq -c
      8 ESTABLISHED
     19 LISTEN
      3 SYN_SENT
      3 TIME_WAIT

有图片的:https://blog.csdn.net/yangshengwei230612/article/details/115165095



另一个详细的:
https://www.jianshu.com/p/42918db85f19 :

在开发网络服务器应用系统的时候,有时会碰到服务器有大量的socket处于CLOSE_WAIT状态,也无法关闭,导致服务器无法接受新的用户请求,最终导致服务器奔溃,系统重启才能解决。

为什么会出现大量的CLOSE_WAIT状态呢?

要解决这个问题,我们得先介绍一下socket断开过程中的四次挥手。

终止TCP连接的四次挥手

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。
假设终止命令由client端发起。

 

 

 

当clien端传输完成数据,或者需要断开连接时:

  1. Client端发送一个FIN报文给Server端。(序号为M)
    1.1. 表示要终止Client到Server这个方向的连接。
    1.1. 通过调用close(socket) API。
    1.3 表示Client不再会发送数据到Server端。(但Server还能继续发给Client端)
    1.4 Client状态变为FIN_WAIT_1
  2. Server端收到FIN后,发送一个ACK报文给Client端。(序号为M+1)
    2.1 Server状态变为CLOSE_WAIT
    2.2 Client收到序号为(M+1)的ACK后状态变为FIN_WAIT_2
    。。。
  3. Server端也发送一个FIN报文给Client端。(序号为N)
    3.1 表示Server也要终止到Client端这个方向的连接。
    3.2. 通过调用close(socket) API。
    3.3 Server端状态变为LAST_ACK
  4. Client端收到报文FIN后,也发送一个ACK报文给服务器。(序号N+1)
    4.1 Client状态变为TIME_WAIT
  5. Server端收到序号为(N+1)的ACK
    5.1 Server的状态变为CLOSED.
  6. 等带2MSL之后
    6.1 Client的状态也变为CLOSE.
  7. 至此,一个完整的TCP连接就关闭了。

两个基本问题:

  1. Q: 我们看到CLOSE_WAIT出现在什么时候呢?
    A: 在Sever端收到Client的FIN消息之后。
  2. Q: 状态CLOSE_WAIT在什么时候转换成下一个状态呢?
    A: 在Server端向Client发送FIN消息之后。

至此似乎明白了为什么会出现CLOSE_WAIT的状态:如果Server端一直没有向client端发送FIN消息(调用close() API),那么这个CLOSE_WAIT会一直存在下去。

原因分析

从上面我们看到出现CLOSE_WAIT,说明Server端没有发起close()操作,这基本上是用户server端程序的问题了;通常情况下,Server都是等待Client访问,如果Client退出请求关闭连接,server端自觉close()对应的连接。

当然这也可能是业务实现上的需要,暂时不发送FIN,因为服务器可能还有数据要发往客户端,等发送完所有应用数据最后再发送FIN消息了;这个场景并不是这里我们讨论的大量COLSE_WAIT的问题了,因为这个还是可控的。

我们要讨论的场景是什么?我们先介绍两个系统调用,前面也提到并且用到的close(socket)和shutdown(socket,HOW)接着往下分析。

我们知道一个进程打开一个socket,然后此进程fork出子进程的时候,父进程已打开的socket是会被继承的,即子进程能够继续访问这个socket。其结果就是,一个socket被两个进程打开,一个父进程和一个子进程,此时socket的引用计数会变成2。

  • 调用close(socket)时,内核先检查socket上的引用计数器:如果引用计数大于1,那么将这个引用计数减1,然后直接返回。如果引用计数等于1,那么内核才会真正关闭此socket。(通过发送FIN到对端来关闭TCP连接)
  • 调用shutdown(socket,HOW)时,内核不会检查此socket对应的引用计数器,直接向对端发送FIN来关闭TCP连接。

据此分析,很大可能性是用户服务器的程序实现有问题导致的大量CLOSE_WAIT的socket,比如父进程打开了socket,然后通过fork出子进程来处理业务,父进程继续对网络请求进行监听,永远不会终止;当客户端发FIN过来的时候,处理业务的子进程处理此FIN消息,调用close()对本端进行关闭,然而这个close()调用只是把socket的引用计数器减1,因为父进程还在运行,socket并没关闭,这样就导致系统中又多了一个CLOSE_WAIT的socket,长此以往,就这样了。

关于TIME_WAIT状态

多说两句关于TIME_WAIT的状态,这个发生在client端,而且是不可避免的,其时间长度是固定的2MSL,到期自动转为CLOSED,不会导致系统资源耗尽的问题。

MSL是一个系统级参数,可调。

 

posted @ 2022-01-04 17:59  狂神314  阅读(1588)  评论(0编辑  收藏  举报