解决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端传输完成数据,或者需要断开连接时:
- 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 - Server端收到FIN后,发送一个ACK报文给Client端。(序号为M+1)
2.1 Server状态变为CLOSE_WAIT
2.2 Client收到序号为(M+1)的ACK后状态变为FIN_WAIT_2
。。。 - Server端也发送一个FIN报文给Client端。(序号为N)
3.1 表示Server也要终止到Client端这个方向的连接。
3.2. 通过调用close(socket) API。
3.3 Server端状态变为LAST_ACK - Client端收到报文FIN后,也发送一个ACK报文给服务器。(序号N+1)
4.1 Client状态变为TIME_WAIT - Server端收到序号为(N+1)的ACK
5.1 Server的状态变为CLOSED. - 等带2MSL之后
6.1 Client的状态也变为CLOSE. - 至此,一个完整的TCP连接就关闭了。
两个基本问题:
- Q: 我们看到CLOSE_WAIT出现在什么时候呢?
A: 在Sever端收到Client的FIN消息之后。 - 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是一个系统级参数,可调。