zoukankan      html  css  js  c++  java
  • 【网络编程】服务端产生大量的close_wait状态的进程分析

    首先要明白close_wait状态是在tcp通信四次握手时的一个中间状态:

                          

    即当被动关闭方发送完ACK后进入的状态。这个状态的结束,即要达到下一个状态LASK_ACK需要在发无端发送完剩余的数据后(send)、调用close函数之后。

    下面我们模拟这种情况,即服务端发送完剩余的数据后,并没有调用close函数:

    client端代码:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <unistd.h>
     5 #include <sys/socket.h>
     6 #include <netinet/in.h>
     7 #include <arpa/inet.h>
     8  
     9  
    10 #define MAXLINE 80
    11 #define SERV_PORT 8000
    12  
    13  
    14 int main(int argc, char *argv[])
    15 {
    16     struct sockaddr_in servaddr;
    17     char str[MAXLINE] = "test ";
    18     int sockfd, n;
    19  
    20  
    21     while(1)
    22     {
    23         sockfd = socket(AF_INET, SOCK_STREAM, 0);
    24  
    25  
    26         bzero(&servaddr, sizeof(servaddr));
    27         servaddr.sin_family = AF_INET;
    28         inet_pton(AF_INET, "192.168.254.26", &servaddr.sin_addr);
    29         servaddr.sin_port = htons(SERV_PORT);
    30  
    31  
    32         connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    33         write(sockfd, str, strlen(str));
    34  
    35  
    36         close(sockfd);
    37         sleep(2);
    38     }
    39  
    40  
    41     return 0;
    42 }

    server端代码:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <iostream>
     5  
     6  
     7 #include <sys/mman.h>
     8 #include <sys/stat.h>        /* For mode constants */
     9 #include <fcntl.h>           /* For O_* constants */
    10  
    11  
    12 #include <unistd.h>
    13 #include <arpa/inet.h>
    14  
    15  
    16 using namespace std;
    17  
    18  
    19 #define LENGTH 128
    20 #include "netinet/in.h"
    21 #define MAXLINE 80
    22 #define SERV_PORT 8000
    23  
    24  
    25 int main(int argc,char** argv)
    26 {
    27     struct sockaddr_in servaddr, cliaddr;
    28     socklen_t cliaddr_len;
    29     int listenfd;
    30     char buf[MAXLINE];
    31     char str[INET_ADDRSTRLEN];
    32     //int i, n;
    33     int  n;
    34  
    35  
    36     //创建socket
    37     listenfd = socket(AF_INET, SOCK_STREAM, 0);
    38  
    39  
    40     //设置端口重用
    41     int opt = 1;
    42     setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    43  
    44  
    45     //fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
    46  
    47  
    48     bzero(&servaddr, sizeof(servaddr));
    49     servaddr.sin_family = AF_INET;
    50  
    51  
    52     inet_pton(AF_INET,"192.168.254.26",&(servaddr.sin_addr.s_addr));
    53     //servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    54     servaddr.sin_port = htons(SERV_PORT);
    55  
    56  
    57     bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    58  
    59  
    60     listen(listenfd, 20);
    61  
    62  
    63     printf("Accepting connections ...
    ");
    64     while (1)
    65     {
    66         cliaddr_len = sizeof(cliaddr);
    67         int connfd = accept(listenfd,
    68                 (struct sockaddr *)&cliaddr, &cliaddr_len);
    69  
    70  
    71         //while(1)
    72         {
    73             n = recv(connfd, buf, MAXLINE,0);
    74             if (n == 0)
    75             {
    76                 //对端主动关闭
    77                 printf("the other side has been closed.
    ");
    78                 //break;
    79             }
    80             printf("received from %s at PORT %d len = %d
    ",
    81                    inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
    82                    ntohs(cliaddr.sin_port),n);
    83         }
    84         //测试:模拟CLOSE_WAIT状态时,将close(connfd);这句代码注释
    85  
    86         //close(connfd);
    87     }
    88  
    89  
    90      return 0;
    91 }

    测试代码中,当recv的返回值为0时(对端主动关闭连接),会跳出while(1)循环,此时正确做法是调用close关闭tcp连接

    此处我们为了测试,故意将close(connfd)这句代码注释掉,注释后服务器对于客户端发送的FIN包不会做回应,一直保持close_wait状态。

    运行截图:

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

    ------------------分割线----------------------

    转自:https://blog.51cto.com/jin771998569/1688253

    一、“多半是程序的原因”?这个还是交给程序猿吧

    二、linux 下 CLOSE_WAIT过多的解决方法

    情景描述:系统产生大量“Too many open files” 

    原因分析:在服务器与客户端通信过程中,因服务器发生了socket未关导致的closed_wait发生,致使监听port打开的句柄数到了1024个,且均处于close_wait的状态,最终造成配置的port被占满出现“Too many open files”,无法再进行通信。 

    close_wait状态出现的原因是被动关闭方未关闭socket造成

    解决办法:有两种措施可行 

    一、解决: 

    原因是因为调用ServerSocket类的accept()方法和Socket输入流的read()方法时会引起线程阻塞,所以应该用setSoTimeout()方法设置超时(缺省的设置是0,即超时永远不会发生);超时的判断是累计式的,一次设置后,每次调用引起的阻塞时间都从该值中扣除,直至另一次超时设置或有超时异常抛出。 

    比如,某种服务需要三次调用read(),超时设置为1分钟,那么如果某次服务三次read()调用的总时间超过1分钟就会有异常抛出,如果要在同一个Socket上反复进行这种服务,就要在每次服务之前设置一次超时。 

    二、规避: 

    调整系统参数,包括句柄相关参数和TCP/IP的参数; 

    注意: 

    /proc/sys/fs/file-max 是整个系统可以打开的文件数的限制,由sysctl.conf控制; 

    ulimit修改的是当前shell和它的子进程可以打开的文件数的限制,由limits.conf控制; 

    lsof是列出系统所占用的资源,但是这些资源不一定会占用打开文件号的;比如:共享内存,信号量,消息队列,内存映射等,虽然占用了这些资源,但不占用打开文件号; 

    因此,需要调整的是当前用户的子进程打开的文件数的限制,即limits.conf文件的配置; 

    如果cat /proc/sys/fs/file-max值为65536或甚至更大,不需要修改该值; 

    若ulimit -a ;其open files参数的值小于4096(默认是1024), 则采用如下方法修改open files参数值为8192;方法如下: 

    1.使用root登陆,修改文件/etc/security/limits.conf 

    vim /etc/security/limits.conf
     

     添加 

    xxx - nofile 8192 

    xxx 是一个用户,如果是想所有用户生效的话换成 * ,设置的数值与硬件配置有关,别设置太大了。 

    #<domain>     <type>   <item>       <value>
    *         soft    nofile    8192 
    *         hard    nofile    8192
     

    #所有的用户每个进程可以使用8192个文件描述符。

     

    2.使这些限制生效 

    确定文件/etc/pam.d/login 和/etc/pam.d/sshd包含如下行: 

    session required pam_limits.so 

    然后用户重新登陆一下即可生效。

     

    3. 在bash下可以使用ulimit -a 参看是否已经修改: 

    一、 修改方法:(暂时生效,重新启动服务器后,会还原成默认值) 

    sysctl -w net.ipv4.tcp_keepalive_time=600   
    sysctl -w net.ipv4.tcp_keepalive_probes=2 
    sysctl -w net.ipv4.tcp_keepalive_intvl=2
     

    注意:Linux的内核参数调整的是否合理要注意观察,看业务高峰时候效果如何。 

    二、 若做如上修改后,可起作用;则做如下修改以便永久生效。 

    vi /etc/sysctl.conf 

    若配置文件中不存在如下信息,则添加: 

    net.ipv4.tcp_keepalive_time = 1800 
    net.ipv4.tcp_keepalive_probes = 3 
    net.ipv4.tcp_keepalive_intvl = 15
     

    编辑完 /etc/sysctl.conf,要重启network 才会生效 

    /etc/rc.d/init.d/network restart 

    然后,执行sysctl命令使修改生效,基本上就算完成了。 

    ------------------------------------------------------------ 

    修改原因: 

    当客户端因为某种原因先于服务端发出了FIN信号,就会导致服务端被动关闭,若服务端不主动关闭socket发FIN给Client,此时服务端Socket会处于CLOSE_WAIT状态(而不是LAST_ACK状态)。通常来说,一个CLOSE_WAIT会维持至少2个小时的时间(系统默认超时时间的是7200秒,也就是2小时)。如果服务端程序因某个原因导致系统造成一堆CLOSE_WAIT消耗资源,那么通常是等不到释放那一刻,系统就已崩溃。因此,解决这个问题的方法还可以通过修改TCP/IP的参数来缩短这个时间,于是修改tcp_keepalive_*系列参数: 

    tcp_keepalive_time: 

    /proc/sys/net/ipv4/tcp_keepalive_time 

    INTEGER,默认值是7200(2小时) 

    当keepalive打开的情况下,TCP发送keepalive消息的频率。建议修改值为1800秒。 

    tcp_keepalive_probes:INTEGER 

    /proc/sys/net/ipv4/tcp_keepalive_probes 

    INTEGER,默认值是9 

    TCP发送keepalive探测以确定该连接已经断开的次数。(注意:保持连接仅在SO_KEEPALIVE套接字选项被打开是才发送.次数默认不需要修改,当然根据情形也可以适当地缩短此值.设置为5比较合适) 

    tcp_keepalive_intvl:INTEGER 

    /proc/sys/net/ipv4/tcp_keepalive_intvl 

    INTEGER,默认值为75 

    当探测没有确认时,重新发送探测的频度。探测消息发送的频率(在认定连接失效之前,发送多少个TCP的keepalive探测包)。乘以tcp_keepalive_probes就得到对于从开始探测以来没有响应的连接杀除的时间。默认值为75秒,也就是没有活动的连接将在大约11分钟以后将被丢弃。(对于普通应用来说,这个值有一些偏大,可以根据需要改小.特别是web类服务器需要改小该值,15是个比较合适的值)。

     

    1. 系统不再出现“Too many open files”报错现象。 

    2. 处于TIME_WAIT状态的sockets不会激长。 

    在 Linux 上可用以下语句看了一下服务器的TCP状态(连接状态数量统计): 

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


    关于TCP中的keepalive机制,参考博客:
    https://www.cnblogs.com/lidabo/p/3804108.html
    http://www.importnew.com/27624.html
    等有时间需要做一下整理。
     


  • 相关阅读:
    设计权限管理系统(十一)
    设计权限管理系统(十)
    设计权限管理系统(九)
    设计权限管理系统(八)
    设计权限管理系统(七)
    设计权限管理系统(六)
    设计权限管理系统(五)
    设计权限管理系统(四)
    设计权限管理系统(三)
    Dom的深度优先遍历和广度优先遍历
  • 原文地址:https://www.cnblogs.com/xuelisheng/p/10611039.html
Copyright © 2011-2022 走看看