• Linux记录-TCP状态以及(TIME_WAIT/CLOSE_WAIT)分析(转载)


    1.TCP握手定理

     2.TCP状态

    l  CLOSED:初始状态,表示TCP连接是“关闭着的”或“未打开的”。

    l  LISTEN :表示服务器端的某个SOCKET处于监听状态,可以接受客户端的连接。

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

    l  SYN_SENT :这个状态与SYN_RCVD 状态相呼应,当客户端SOCKET执行connect()进行连接时,它首先发送SYN报文,然后随即进入到SYN_SENT 状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT 状态表示客户端已发送SYN报文。

    l  ESTABLISHED :表示TCP连接已经成功建立。

    l  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看到。

    l  FIN_WAIT_2 :上面已经解释了这种状态的由来,实际上FIN_WAIT_2状态下的SOCKET表示半连接,即有一方调用close()主动要求关闭连接。注意:FIN_WAIT_2 是没有超时的(不像TIME_WAIT 状态),这种状态下如果对方不关闭(不配合完成4次挥手过程),那这个 FIN_WAIT_2 状态将一直保持到系统重启,越来越多的FIN_WAIT_2 状态会导致内核crash。

    l  TIME_WAIT :表示收到了对方的FIN报文,并发送出了ACK报文。 TIME_WAIT状态下的TCP连接会等待2*MSL(Max Segment Lifetime,最大分段生存期,指一个TCP报文在Internet上的最长生存时间。每个具体的TCP协议实现都必须选择一个确定的MSL值,RFC 1122建议是2分钟,但BSD传统实现采用了30秒,Linux可以cat /proc/sys/net/ipv4/tcp_fin_timeout看到本机的这个值),然后即可回到CLOSED 可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。(这种情况应该就是四次挥手变成三次挥手的那种情况)

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

    l  CLOSE_WAIT :表示正在等待关闭。怎么理解呢?当对方close()一个SOCKET后发送FIN报文给自己,你的系统毫无疑问地将会回应一个ACK报文给对方,此时TCP连接则进入到CLOSE_WAIT状态。接下来呢,你需要检查自己是否还有数据要发送给对方,如果没有的话,那你也就可以close()这个SOCKET并发送FIN报文给对方,即关闭自己到对方这个方向的连接。有数据的话则看程序的策略,继续发送或丢弃。简单地说,当你处于CLOSE_WAIT 状态下,需要完成的事情是等待你去关闭连接。

    l  LAST_ACK :当被动关闭的一方在发送FIN报文后,等待对方的ACK报文的时候,就处于LAST_ACK 状态。当收到对方的ACK报文后,也就可以进入到CLOSED 可用状态了。

    #######################################################################################################################################

    LISTEN:等待从任何远端TCP 和端口的连接请求。 SYN_SENT:发送完一个连接请求后等待一个匹配的连接请求。 SYN_RECEIVED:发送连接请求并且接收到匹配的连接请求以后等待连接请求确认。 ESTABLISHED:表示一个打开的连接,接收到的数据可以被投递给用户。连接的数据传输阶段的正常状态。 FIN_WAIT_1:等待远端TCP 的连接终止请求,或者等待之前发送的连接终止请求的确认。 FIN_WAIT_2:等待远端TCP 的连接终止请求。 CLOSE_WAIT:等待本地用户的连接终止请求。 CLOSING:等待远端TCP 的连接终止请求确认。 LAST_ACK:等待先前发送给远端TCP 的连接终止请求的确认(包括它字节的连接终止请求的确认) TIME_WAIT:等待足够的时间过去以确保远端TCP 接收到它的连接终止请求的确认。 TIME_WAIT 两个存在的理由: 1.可靠的实现tcp全双工连接的终止; 2.允许老的重复分节在网络中消逝。 CLOSED:不在连接状态(这是为方便描述假想的状态,实际不存在)

    ######################################################################################################################################

    3.服务器大量TIME_WAIT和CLOSE_WAIT分析

    #查看TCP状态: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 表示被动关闭,Listen表示正在监听可以接受客户端连接。

    #常见问题分析

    1.服务器保持了大量TIME_WAIT状态

    2.服务器保持了大量CLOSE_WAIT状态

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

    #############################################################################

    tomcat环境下服务器文件句柄耗尽(Too Many Open Files)的问题排查

    为什么会出现文件句柄耗尽的情况?

    主要是因为linux在文件句柄的数目上有两个级别的限制。一个是系统级别的总数限制,一个是针对用户的限制。默认情况下每个用户所能使用的句柄数是1024。一般情况下1024也够用了,但是在大容量的系统上,特别是会频繁使用网络通信和文件IO的系统上,1024很快就被耗光了。所以首先我们要调整这个值。修改方法如下:

    1. ulimit -a 查看当前用户的文件句柄限制  

    2. 用户级别的句柄数限制修改 

    修改 /etc/security/limits.conf 增加下面的代码:  

    用户名(或者用*表示所有用户)  soft nofile 65535    

    用户名 hard nofile 65535   

    有两种限制,一种是soft软限制,在数目超过软限制的时候系统会给出warning警告,但是达到hard硬限制的时候系统将拒绝或者异常了。  

    修改之后可能需要重启shell生效。  

    3. 系统级别的句柄数限制修改

    sysctl -w fs.file-max 65536  

    或者  echo "65536" > /proc/sys/fs/file-max  

    两者作用是相同的,前者改内核参数,后者直接作用于内核参数在虚拟文件系统(procfs, psuedo file system)上对应的文件而已。  

    可以用下面的命令查看新的限制  

    sysctl -a | grep fs.file-max  

    或者  cat /proc/sys/fs/file-max  

    4.修改内核参数  

    /etc/sysctl.conf  

    echo "fs.file-max=65536" >> /etc/sysctl.conf  

    sysctl -p  

    查看系统总限制 命令:cat /proc/sys/fs/file-max    

    查看整个系统目前使用的文件句柄数量命令:cat /proc/sys/fs/file-nr   

    查看某个进程开了哪些句柄 :lsof -p pid    

    某个进程开了几个句柄 :lsof -p pid |wc -l    

    也可以看到某个目录 /文件被什么进程占用了,显示已打开该目录或文件的所有进程信息 :lsof path/filename   

    具体这个值应该设置成多少?

    优先级(Open File Descriptors):
    soft limit < hard limit < kernel < 实现最大file descriptor数采用的数据结构所导致的限制

    其实这个值倒是没有具体限制,但是分配的值如果太大反而会影响系统性能,所以要根据具体应用调配权衡。

    问题的解决方案:

    首先当然是修改linux句柄数限制到一个合适的值。

    然后就是应用本身的一个调整。有这么几种情况:

    1.数据库连接池的优化。必须要使用连接池,否则句柄没耗光数据库就崩了。。。

    2.抓取资源的时候有可能会用到HttpClient,尽量也应该使用连接池来控制连接数。

    3.连接池设置的把握,建立连接超时时间,读取超时时间,连接数目,等待时间,等都需要配置到一个合适的值,否则发挥不出连接池的性能。

    ###########################################################################################################

    解决思路很简单,就是让服务器能够快速回收和重用那些TIME_WAIT的资源。

    对/etc/sysctl.conf文件的修改:

    #对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间   

    net.ipv4.tcp_syn_retries=2  

    #net.ipv4.tcp_synack_retries=2  

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

    net.ipv4.tcp_keepalive_time=1200  

    net.ipv4.tcp_orphan_retries=3  

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

    net.ipv4.tcp_fin_timeout=30    

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

    net.ipv4.tcp_max_syn_backlog = 4096  

    #表示开启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  

    ##减少超时前的探测次数   

    net.ipv4.tcp_keepalive_probes=5   

    ##优化网络设备接收队列   

    net.core.netdev_max_backlog=3000   

    修改完之后执行/sbin/sysctl -p让参数生效。
    这里头主要注意到的是net.ipv4.tcp_tw_reuse
    net.ipv4.tcp_tw_recycle
    net.ipv4.tcp_fin_timeout
    net.ipv4.tcp_keepalive_*
    这几个参数。
    net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle的开启都是为了回收处于TIME_WAIT状态的资源。
    net.ipv4.tcp_fin_timeout这个时间可以减少在异常情况下服务器从FIN-WAIT-2转到TIME_WAIT的时间。
    net.ipv4.tcp_keepalive_*一系列参数,是用来设置服务器检测连接存活的相关配置。

    如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在服务器程序里头啊。

  • 相关阅读:
    BZOJ 1576 树剖+LCT
    CF1051D Bicolorings 递推
    CF938D Buy a Ticket dijkstra
    记一次创建svc代理失败
    K8S中Service
    K8S中的Job和CronJob
    K8S中DaemonSet
    Linux expect介绍和用法
    Java根据余弦定理计算文本相似度
    Python和Sublime的整合
  • 原文地址:https://www.cnblogs.com/xinfang520/p/8961129.html
Copyright © 2020-2023  润新知