网络编程-listen函数

来源:互联网 发布:知乎 股市入门书籍 编辑:程序博客网 时间:2024/06/14 10:38
listen()函数
  假如你不希望与远程的一个地址相连,或者说, 仅仅是将它踢开,那你就需要等待接入请求并且用各种方法处理它们。处 理过程分两步:首先,你听--listen(),然后,你接受--accept() (请看下面的 内容)。
除了要一点解释外,系统调用 listen 也相当简单。
int listen(int sockfd, int backlog); 
sockfd 是调用 socket() 返回的套接字文件描述符。backlog 是在进入 队列中允许的连接数目。什么意思呢? 进入的连接是在队列中一直等待直 到你接受 (accept() 请看下面的文章)连接。它们的数目限制于队列的允许。 大多数系统的允许数目是20,你也可以设置为5到10。
和别的函数一样,在发生错误的时候返回-1,并设置全局错误变量 errno。
你可能想象到了,在你调用 listen() 前你或者要调用 bind() 或者让内 核随便选择一个端口。如果你想侦听进入的连接,那么系统调用的顺序可 能是这样的: 
socket(); 
  bind(); 
listen(); 
  /* accept() 应该在这 */ 

因为它相当的明了,我将在这里不给出例子了。(在 accept() 那一章的 代码将更加完全。)真正麻烦的部分在 accept()。



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个客户端与服务器建立连接,查看连接的建立情况。

服务器代码:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include <stdio.h>  
  2. #include<unistd.h>  
  3. #include<sys/types.h>       /* basic system data types */  
  4. #include<sys/socket.h>      /* basic socket definitions */  
  5. #include<netinet/in.h>      /* sockaddr_in{} and other Internet defns */  
  6. #include<arpa/inet.h>       /* inet(3) functions */  
  7. #include<sys/epoll.h>       /* epoll function */  
  8. #include<fcntl.h>  
  9. #include<stdlib.h>  
  10. #include<errno.h>  
  11. #include<stdio.h>  
  12. #include<string.h>  
  13.   
  14.   
  15. int main(int argc,char*argv[])  
  16. {  
  17.     int listenfd,connfd;  
  18.     struct sockaddr_in cliaddr,servaddr;  
  19.     int queuelen=5;  
  20.   
  21.     if(argc!=2){  
  22.         puts("usage# ./aworker listenqueuelen");  
  23.         exit(0);  
  24.     }     
  25.     queuelen=atoi(argv[1]);  
  26.   
  27.     listenfd = socket(AF_INET,SOCK_STREAM,0);  
  28.   
  29.     bzero(&servaddr,sizeof(servaddr));  
  30.     servaddr.sin_family = AF_INET;  
  31.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  32.     servaddr.sin_port = htons(2989);  
  33.       
  34.     bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));  
  35.       
  36.     listen(listenfd,queuelen);  
  37.     sleep(60); //将这个注释,会出现另一种情况哟~~    
  38.     while(1)  
  39.     {  
  40.         connfd = accept(listenfd,NULL,0);  
  41.         if(connfd == -1)  
  42.         {  
  43.             perror("accept error");  
  44.             continue;  
  45.         }  
  46.         puts("new connection...");  
  47.     }  
  48.     return 0;  
  49. }  

client代码
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. #include "client.h"  
  2.   
  3. //void cli_hander(int sockfd,)  
  4.   
  5. int main()  
  6. {  
  7.     int sockfd;  
  8.     int rc;   
  9.     int cpid;  
  10.     struct sockaddr_in servaddr;  
  11.       
  12.     bzero(&servaddr,sizeof(servaddr));  
  13.     servaddr.sin_family = AF_INET;  
  14.     inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr);  
  15.     servaddr.sin_port = htons(2989);  
  16.       
  17.     for(int i=0;i<20;i++)  
  18.     {     
  19.         cpid = fork();  
  20.         if(cpid == 0)  
  21.         {     
  22.             sockfd = socket(AF_INET,SOCK_STREAM,0);  
  23.             rc = connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));  
  24.             if(rc == -1)   
  25.             {     
  26.                 perror("connect error");  
  27.                 exit(0);  
  28.             }     
  29.             printf("pid#%d connected...\n",getpid());  
  30.             sleep(3);  
  31.             close(sockfd);  
  32.             exit(0);  
  33.         }     
  34.     }     
  35.   
  36.     while(1)  
  37.     {     
  38.         cpid = wait(NULL);  
  39.         if(cpid==-1){  
  40.             perror("end of wait");  
  41.             break;  
  42.         }  
  43.         printf("pid#%d exit...\n",cpid);  
  44.     }  
  45.     return 0;  
  46. }  

实验结果:

服务器端显示:

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. root@cloud2:~/slp/NetWrokProgram/server# ./aworker 2  
  2. new connection...  
  3. new connection...  
  4. new connection...  
  5. new connection...  
  6. new connection...  

客户端显示:
[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
  1. root@cloud2:~/slp/NetWrokProgram/client# ./a.out   
  2. pid#16697 connected...  
  3. pid#16699 connected...  
  4. pid#16698 connected...  
  5. pid#16697 exit...  
  6. pid#16699 exit...  
  7. pid#16698 exit...  
  8. pid#16700 connected...  
  9. pid#16701 connected...  
  10. pid#16700 exit...  
  11. pid#16701 exit...  
  12. connect error: Connection timed out  
  13. connect error: Connection timed out  
  14. connect error: Connection timed out  
  15. connect error: Connection timed out  
  16. connect error: Connection timed out  
  17. connect error: Connection timed out  
  18. connect error: Connection timed out  
  19. connect error: Connection timed out  
  20. connect error: Connection timed out  
  21. connect error: Connection timed out  
  22. connect error: Connection timed out  
  23. connect error: Connection timed out  
  24. connect error: Connection timed out  
  25. connect error: Connection timed out  
  26. connect error: Connection timed out  
  27. pid#16702 exit...  
  28. pid#16703 exit...  
  29. pid#16704 exit...  
  30. pid#16705 exit...  
  31. pid#16706 exit...  
  32. pid#16707 exit...  
  33. pid#16708 exit...  
  34. pid#16709 exit...  
  35. pid#16710 exit...  
  36. pid#16711 exit...  
  37. pid#16712 exit...  
  38. pid#16713 exit...  
  39. pid#16714 exit...  
  40. pid#16715 exit...  
  41. pid#16716 exit...  
  42. end of wait: No child processes  

结果分析:

同时建立连接的客户端进程共有20个,可是只有5个完成了连接的建立,其他15个没有成功。有趣的是,建立的5个链接中有3个是马上建立的,2个是过了一段时间后后来才建立的。

实例分析2

将server端的代码中的sleep(60)注释,即服务端listen即开始进入while循环中的accept阻塞:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. ...  
  2. listen(listenfd,queuelen);  
  3. sleep(60); //将这个注释,会出现另一种情况哟~~    
  4. while(1)  
  5. {  
  6.     connfd = accept(listenfd,NULL,0);  
  7.     ....  

同样的运行,结果如下:

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. root@cloud2:~/slp/NetWrokProgram/server# ./aworker 2  
  2. new connection...  
  3. new connection...  
  4. new connection...  
  5. new connection...  
  6. new connection...  
  7. new connection...  
  8. new connection...  
  9. new connection...  
  10. new connection...  
  11. new connection...  
  12. new connection...  
  13. new connection...  
  14. new connection...  
  15. new connection...  
  16. new connection...  
  17. new connection...  
  18. new connection...  
  19. new connection...  
  20. new connection...  
  21. new connection...  

客户端:
[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. root@cloud2:~/slp/NetWrokProgram/client# ./a.out   
  2. pid#16736 connected...  
  3. pid#16737 connected...  
  4. pid#16738 connected...  
  5. pid#16739 connected...  
  6. pid#16740 connected...  
  7. pid#16741 connected...  
  8. pid#16742 connected...  
  9. pid#16743 connected...  
  10. pid#16744 connected...  
  11. pid#16745 connected...  
  12. pid#16746 connected...  
  13. pid#16747 connected...  
  14. pid#16748 connected...  
  15. pid#16749 connected...  
  16. pid#16750 connected...  
  17. pid#16751 connected...  
  18. pid#16752 connected...  
  19. pid#16753 connected...  
  20. pid#16755 connected...  
  21. pid#16754 connected...  
  22. pid#16736 exit...  
  23. pid#16737 exit...  
  24. pid#16738 exit...  
  25. pid#16739 exit...  
  26. pid#16740 exit...  
  27. pid#16741 exit...  
  28. pid#16742 exit...  
  29. pid#16743 exit...  
  30. pid#16744 exit...  
  31. pid#16745 exit...  
  32. pid#16746 exit...  
  33. pid#16747 exit...  
  34. pid#16748 exit...  
  35. pid#16749 exit...  
  36. pid#16750 exit...  
  37. pid#16751 exit...  
  38. pid#16752 exit...  
  39. pid#16753 exit...  
  40. pid#16755 exit...  
  41. pid#16754 exit...  
  42. end of wait: No child processes  

结果分析:
由于每个连接在建立之后,已完成队列中的连接马上就被accept给读取了,所以已完成和未完成队列中的连接数之和根本不可能超过backlog限定的个数。

原文链接:
http://blog.csdn.net/ordeder/article/details/21551567 
0 0
原创粉丝点击