背景知识
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个是过了一段时间后后来才建立的。
实例分析2
将server端的代码中的sleep(60)注释,即服务端listen即开始进入while循环中的accept阻塞:
...
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
结果分析:
由于每个连接在建立之后,已完成队列中的连接马上就被accept给读取了,所以已完成和未完成队列中的连接数之和根本不可能超过backlog限定的个数。
原文链接:
http://blog.csdn.net/ordeder/article/details/21551567