服务器的TIME_WAIT和CLOSE_WAIT

本文深入解析TIME_WAIT和CLOSE_WAIT状态在网络连接关闭过程中的作用,探讨其在服务器维护中的重要性和潜在问题,同时提供有效的解决方案,帮助读者理解并解决常见的网络连接异常。

在服务器的日常维护过程中,排查TIME_WAIT和CLOSE_WAIT问题需要用到下面的命令:

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'


TIME_WAIT 814
CLOSE_WAIT 1
FIN_WAIT1 1
ESTABLISHED 634
SYN_RECV 2
LAST_ACK 1

常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

查看网络状态,如果出现以下两种问题,服务器一般都出了异常。

  • 服务器保持了大量TIME_WAIT状态
  • 服务器保持了大量CLOSE_WAIT状态

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

什么是TIME-WAIT和CLOSE-WAIT ?

由于socket是全双工的工作模式,一个socket的关闭,是需要四次握手来完成的:

  1. 主动关闭连接的一方,调用close();协议层发送FIN包 ;
  2. 被动关闭的一方收到FIN包后,协议层回复ACK;然后被动关闭的一方,进入CLOSE_WAIT状态,主动关闭的一方等待对方关闭,则进入FIN_WAIT_2状态;此时,主动关闭的一方等待被动关闭一方的应用程序,调用close操作 ;
  3. 被动关闭的一方在完成所有数据发送后,调用close()操作;此时,协议层发送FIN包给主动关闭的一方,等待对方的ACK,被动关闭的一方进入LAST_ACK状态;
  4.  主动关闭的一方收到FIN包,协议层回复ACK;此时,主动关闭连接的一方,进入TIME_WAIT状态;而被动关闭的一方,进入CLOSED状态 ;
  5. 等待2MSL时间,主动关闭的一方,结束TIME_WAIT,进入CLOSED状态 ; 

通过上面的一次socket关闭操作,可以得出以下几点:

  1. 主动关闭连接的一方 – 也就是主动调用socket的close操作的一方,最终会进入TIME_WAIT状态 ;
  2. 被动关闭连接的一方,有一个中间状态,即CLOSE_WAIT,因为协议层在等待上层的应用程序,主动调用close操作后才主动关闭这条连接 ;
  3. TIME_WAIT会默认等待2MSL时间后,才最终进入CLOSED状态;
  4. 在一个连接没有进入CLOSED状态之前,这个连接是不能被重用的

TIME_WAIT并不可怕,CLOSE_WAIT才可怕,因为CLOSE_WAIT很多,表示说要么是你的应用程序写的有问题,没有合适的关闭socket。要么是服务器CPU处理不过来(CPU太忙)或者你的应用程序一直睡眠到其它地方(锁,或者文件I/O等等),你的应用程序获得不到合适的调度时间,造成你的程序没法真正的执行close操作。

处理方法

服务器保持了大量TIME_WAIT状态

这种情况比较常见,一些爬虫服务器或者WEB服务器上经常会遇到这个问题。

TIME_WAIT是主动关闭连接的一方保持的状态,对于爬虫服务器来说他本身就是“客户端”,在完成一个任务之后,他就会发起主动关闭连接,从而进入TIME_WAIT的状态,然后在保持这个状态2MSL(max segment lifetime)时间之后,彻底关闭回收资源。

这样做的主要出于以下两个方面的考虑:

  • 防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失),可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。
  • 基于TCP的HTTP协议,关闭TCP连接的是Server端,这样,Server端会进入TIME_WAIT状态,可 想而知,对于访 问量大的Web Server,会存在大量的TIME_WAIT状态,假如server一秒钟接收1000个请求,那么就会积压 240*1000=240,000个 TIME_WAIT的记录,维护这些状态给Server带来负担。当然现代操作系统都会用快速的查找算法来管理这些 TIME_WAIT,所以对于新的 TCP连接请求,判断是否hit中一个TIME_WAIT不会太费时间,但是有这么多状态要维护总是不好。  

主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。

解决方案

  • 通过修改/etc/sysctl.conf文件,服务器能够快速回收和重用那些TIME_WAIT的资源。
#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭    
net.ipv4.tcp_syncookies = 1    
#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭    
net.ipv4.tcp_tw_reuse = 1    
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭    
net.ipv4.tcp_tw_recycle = 1  
#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间    
net.ipv4.tcp_fin_timeout=30  

服务器保持了大量的close_wait状态

time_wait问题可以通过调整内核参数和适当的设置web服务器的keep-Alive值来解决。因为time_wait是自己可控的,要么就是对方连接的异常,要么就是自己没有快速的回收资源,总之不是由于自己程序错误引起的。但是close_wait就不一样了,服务器保持大量的close_wait只有一种情况,那就是对方发送一个FIN后,程序自己这边没有进一步发送ACK以确认。换句话说就是在对方关闭连接后,程序里没有检测到,或者程序里本身就已经忘了这个时候需要关闭连接,于是这个资源就一直被程序占用着。

解决方案

  • 关闭正在运行的程序,这个需要视业务情况而定。
  • 尽快的修改程序里的bug,然后提交到线上服务器。
### TCP协议中TIME_WAITCLOSE_WAIT状态的作用区别 在TCP连接的生命周期中,`TIME_WAIT` `CLOSE_WAIT` 是两个重要的状态,它们分别出现在连接关闭的不同阶段,承担着不同的作用。 #### TIME_WAIT状态的作用形成 `TIME_WAIT` 状态通常出现在主动关闭连接的一方。当一个TCP连接被主动关闭时,发送FIN的一方会进入FIN-WAIT-1状态,然后根据对方的响应进入FIN-WAIT-2或直接进入TIME-WAIT状态。`TIME_WAIT` 状态的主要作用是确保最后一个确认(ACK)能够到达对方,以及防止旧的连接报文段在网络中消失之前,新的连接不会误认为这些报文段是属于当前连接的。这种状态会持续2MSL(Maximum Segment Lifetime)的时间,在这段时间内,任何来自旧连接的报文段都会被丢弃[^1]。 #### CLOSE_WAIT状态的作用形成 `CLOSE_WAIT` 状态则是在被动关闭连接的情况下形成的。当服务器端收到客户端发送的FIN时,它会发送一个ACK作为回应,此时服务器端的TCP连接进入`CLOSE_WAIT`状态。这个状态表明服务器端已经接收到客户端的FIN,并且已经确认,但是服务器端还没有完成对这个连接的关闭操作。如果服务器端不执行close()操作,那么该连接将一直停留在`CLOSE_WAIT`状态,这可能导致系统中存在大量的`CLOSE_WAIT`状态连接[^1]。 #### 区别 `TIME_WAIT` `CLOSE_WAIT` 的主要区别在于它们在TCP连接关闭过程中的位置作用。`TIME_WAIT` 是主动关闭连接的一方必须经历的状态,它确保了连接的可靠关闭,而`CLOSE_WAIT` 是被动关闭连接的一方在接收到对方的FIN后进入的状态,它表示等待应用程序关闭连接。如果服务器端的应用程序没有正确关闭连接,那么连接可能会无限期地停留在`CLOSE_WAIT`状态,这可能表明服务器端的代码存在问题,需要检查并确保在接收到FIN后能够正确地关闭连接[^1]。 为了防止`CLOSE_WAIT`状态的连接累积,服务器端的应用程序需要及时处理接收到的FIN信号,并执行相应的关闭操作。此外,可以通过调整内核参数来优化TCP连接的管理,但最终的解决方案还是在于应用程序本身的设计实现[^3]。 ```python # 示例代码:模拟服务器端接收到FIN后关闭连接的操作 def handle_client_connection(connection): try: while True: data = connection.recv(1024) if not data: break # 处理数据... finally: connection.close() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值