服务器出现大量listen状态进程,【网络编程】服务端产生大量的close_wait状态的进程分析...

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

fe9695151a50339ab9f73cd9b80d719d.png

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

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

client端代码:

1 #include

2 #include

3 #include

4 #include

5 #include

6 #include

7 #include

8

9

10 #define MAXLINE 80

11 #define SERV_PORT 8000

12

13

14 int main(int argc, char *argv[])15 {16     structsockaddr_in servaddr;17     char str[MAXLINE] = "test";18     intsockfd, 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

2 #include

3 #include

4 #include

5

6

7 #include

8 #include         /*For mode constants*/

9 #include            /*For O_* constants*/

10

11

12 #include

13 #include

14

15

16 using namespacestd;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 structsockaddr_in servaddr, cliaddr;28 socklen_t cliaddr_len;29     intlistenfd;30     charbuf[MAXLINE];31     charstr[INET_ADDRSTRLEN];32     //int i, n;

33     intn;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 ...\n");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.\n");78             //break;

79 }80         printf("received from %s at PORT %d len = %d\n",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状态。

运行截图:

7f50abd501749be7db39b4e63fcefa25.png

如果将大量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 是一个用户,如果是想所有用户生效的话换成 * ,设置的数值与硬件配置有关,别设置太大了。

#

* 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

等有时间需要做一下整理。

原文:https://www.cnblogs.com/xuelisheng/p/10611039.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以帮您编写一个proactor的网络socket服务端c程序。以下是代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/epoll.h> #define MAX_EVENTS 10 #define PORT 8888 int setnonblocking(int fd) { int flags = fcntl(fd, F_GETFL, ); if (flags == -1) { return -1; } flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) { return -1; } return ; } int main() { int listen_fd, conn_fd, epoll_fd, nfds, n, i; struct sockaddr_in serv_addr, cli_addr; socklen_t cli_len = sizeof(cli_addr); struct epoll_event ev, events[MAX_EVENTS]; listen_fd = socket(AF_INET, SOCK_STREAM, ); if (listen_fd == -1) { perror("socket"); exit(EXIT_FAILURE); } memset(&serv_addr, , sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(PORT); if (bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) { perror("bind"); exit(EXIT_FAILURE); } if (listen(listen_fd, SOMAXCONN) == -1) { perror("listen"); exit(EXIT_FAILURE); } epoll_fd = epoll_create1(); if (epoll_fd == -1) { perror("epoll_create1"); exit(EXIT_FAILURE); } ev.events = EPOLLIN; ev.data.fd = listen_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev) == -1) { perror("epoll_ctl: listen_fd"); exit(EXIT_FAILURE); } while (1) { nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1); if (nfds == -1) { perror("epoll_wait"); exit(EXIT_FAILURE); } for (i = ; i < nfds; i++) { if (events[i].data.fd == listen_fd) { conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_len); if (conn_fd == -1) { perror("accept"); continue; } if (setnonblocking(conn_fd) == -1) { perror("setnonblocking"); exit(EXIT_FAILURE); } ev.events = EPOLLIN | EPOLLET; ev.data.fd = conn_fd; if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &ev) == -1) { perror("epoll_ctl: conn_fd"); exit(EXIT_FAILURE); } } else { do { n = read(events[i].data.fd, buf, sizeof(buf)); if (n == -1) { if (errno != EAGAIN) { perror("read"); epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL); close(events[i].data.fd); } break; } else if (n == ) { epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL); close(events[i].data.fd); break; } if (write(events[i].data.fd, buf, n) == -1) { perror("write"); epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL); close(events[i].data.fd); break; } } while (1); } } } return ; } ``` 希望这个程序能够帮助到您。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值