Unix网络编程描述如下:
总结
0. accept()函数不参与三次握手,而只负责从已建立连接队列中取出一个连接和sockfd进行绑定;
1. backlog参数决定了未完成队列和已完成队列中连接数目之和的最大值(从内核角度看,是否这个和就是等于sock->recv_queue ?);
2. accept()函数调用,会从已连接队列中取出一个“连接”(可以是一个描述连接的数据结构,listensocket->sock->recv_queue[sk_buff] ? ),未完成队列和已完成队列中连接数目 之和将减少1;即accept将监听套接字对应的sock的接收队列中的已建立连接的sk_buff取下(从该sk_buff中可以获得对端主机的发送过来的tcp/ip数据包)
3. 监听套接字的已完成队列中的元素个数大于0,那么该套接字是可读的。
4. 当程序调用accept的时候(设置阻塞参数),那么判定该套接字是否可读,不可读则进入睡眠,直至已完成队列中的元素个数大于0(监听套接字可读)而唤起监听进程。
实例分析1
将服务器端的listen函数backlog设置为2,用20个客户端与服务器建立连接,查看连接的建立情况。
服务器代码:
#include <stdio.h> #include<unistd.h> #include<sys/types.h> /* basic system data types */ #include<sys/socket.h> /* basic socket definitions */ #include<netinet/in.h> /* sockaddr_in{} and other Internet defns */ #include<arpa/inet.h> /* inet(3) functions */ #include<sys/epoll.h> /* epoll function */ #include<fcntl.h> #include<stdlib.h> #include<errno.h> #include<stdio.h> #include<string.h> int main(int argc,char*argv[]) { int listenfd,connfd; struct sockaddr_in cliaddr,servaddr; int queuelen=5; if(argc!=2){ puts("usage# ./aworker listenqueuelen"); exit(0); } queuelen=atoi(argv[1]); listenfd = socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(2989); bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)); listen(listenfd,queuelen); sleep(60); //将这个注释,会出现另一种情况哟~~ while(1) { connfd = accept(listenfd,NULL,0); if(connfd == -1) { perror("accept error"); continue; } puts("new connection..."); } return 0; }
client代码
#include "client.h" //void cli_hander(int sockfd,) int main() { int sockfd; int rc; int cpid; struct sockaddr_in servaddr; bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family = AF_INET; inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr); servaddr.sin_port = htons(2989); for(int i=0;i<20;i++) { cpid = fork(); if(cpid == 0) { sockfd = socket(AF_INET,SOCK_STREAM,0); rc = connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)); if(rc == -1) { perror("connect error"); exit(0); } printf("pid#%d connected...\n",getpid()); sleep(3); close(sockfd); exit(0); } } while(1) { cpid = wait(NULL); if(cpid==-1){ perror("end of wait"); break; } printf("pid#%d exit...\n",cpid); } return 0; }
实验结果:
服务器端显示:
- root@cloud2:~/slp/NetWrokProgram/server# ./aworker 2
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
客户端显示:
- root@cloud2:~/slp/NetWrokProgram/client# ./a.out
- pid#16697 connected...
- pid#16699 connected...
- pid#16698 connected...
- pid#16697 exit...
- pid#16699 exit...
- pid#16698 exit...
- pid#16700 connected...
- pid#16701 connected...
- pid#16700 exit...
- pid#16701 exit...
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- connect error: Connection timed out
- pid#16702 exit...
- pid#16703 exit...
- pid#16704 exit...
- pid#16705 exit...
- pid#16706 exit...
- pid#16707 exit...
- pid#16708 exit...
- pid#16709 exit...
- pid#16710 exit...
- pid#16711 exit...
- pid#16712 exit...
- pid#16713 exit...
- pid#16714 exit...
- pid#16715 exit...
- pid#16716 exit...
- end of wait: No child processes
结果分析:
同时建立连接的客户端进程共有20个,可是只有5个完成了连接的建立,其他15个没有成功。有趣的是,建立的5个链接中有3个是马上建立的,2个是过了一段时间后后来才建立的。
客户端需要net.ipv4.tcp_syn_retries = 0,默认net.ipv4.tcp_syn_retries=5, 否则客户端看到的结果是:
pid#21589 connected...
pid#21590 connected...
pid#21591 connected...
pid#21588 connected...
pid#21592 connected...
pid#21593 connected...
pid#21589 exit...
pid#21590 exit...
pid#21591 exit...
pid#21588 exit...
pid#21592 exit...
pid#21593 exit...
pid#21594 connected...
pid#21595 connected...
pid#21594 exit...
pid#21595 exit...
pid#21596 connected...
pid#21587 connected...
pid#21587 exit...
pid#21596 exit...
pid#21597 connected...
pid#21598 connected...
pid#21599 connected...
pid#21600 connected...
pid#21601 connected...
pid#21602 connected...
pid#21603 connected...
pid#21604 connected...
pid#21605 connected...
pid#21586 connected...
pid#21597 exit...
pid#21598 exit...
pid#21599 exit...
pid#21586 exit...
pid#21600 exit...
pid#21601 exit...
pid#21602 exit...
pid#21603 exit...
pid#21604 exit...
pid#21605 exit...
end of wait: No child processes
实例分析2
- ...
- listen(listenfd,queuelen);
- sleep(60); //将这个注释,会出现另一种情况哟~~
- while(1)
- {
- connfd = accept(listenfd,NULL,0);
- ....
同样的运行,结果如下:
- root@cloud2:~/slp/NetWrokProgram/server# ./aworker 2
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
- new connection...
客户端:
- root@cloud2:~/slp/NetWrokProgram/client# ./a.out
- pid#16736 connected...
- pid#16737 connected...
- pid#16738 connected...
- pid#16739 connected...
- pid#16740 connected...
- pid#16741 connected...
- pid#16742 connected...
- pid#16743 connected...
- pid#16744 connected...
- pid#16745 connected...
- pid#16746 connected...
- pid#16747 connected...
- pid#16748 connected...
- pid#16749 connected...
- pid#16750 connected...
- pid#16751 connected...
- pid#16752 connected...
- pid#16753 connected...
- pid#16755 connected...
- pid#16754 connected...
- pid#16736 exit...
- pid#16737 exit...
- pid#16738 exit...
- pid#16739 exit...
- pid#16740 exit...
- pid#16741 exit...
- pid#16742 exit...
- pid#16743 exit...
- pid#16744 exit...
- pid#16745 exit...
- pid#16746 exit...
- pid#16747 exit...
- pid#16748 exit...
- pid#16749 exit...
- pid#16750 exit...
- pid#16751 exit...
- pid#16752 exit...
- pid#16753 exit...
- pid#16755 exit...
- pid#16754 exit...
- end of wait: No child processes
结果分析:
backlog
The maximum length of the queue of pending connections. If set to SOMAXCONN, the underlying service provider responsible for socket s will set the backlog to a maximum reasonable value. There is no standard provision to obtain the actual backlog value.
- /proc/sys/net/core/somaxconn: Limit of socket listen() backlog, known in userspace as SOMAXCONN. Defaults to 128. The value should be raised substantially to support bursts of request. For example, to support a burst of 1024 requests, set somaxconn to 1024.
即使listen(listenfd, SOMAXCONN); 并且设置 net.core.somaxconn = 620
ss -aln
LISTEN 0 128 *:2989 *:*
依然是128,所以必须同时设置listen(listenfd, 600); 并且设置 net.core.somaxconn = 620