• 解决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是一个系统级参数,可调。

    严以律己、宽以待人
  • 相关阅读:
    C++学习的小Tips
    搭建一个简单struts2框架的登陆
    Eclipse启动tomcat,http://localhost:8080/无法访问的解决方法
    JAVA解析XML的四种方法
    正则表达式学习笔记(附:Java版示例代码)
    Windows下几个常用的和进程有关的命令
    Java网络编程学习
    项目新增内存表优化软件速度
    Android三种消息提示
    数字手写识别——Java实现KNN算法
  • 原文地址:https://www.cnblogs.com/kuang17/p/15763481.html
Copyright © 2020-2023  润新知