免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3530 | 回复: 4
打印 上一主题 下一主题

[Linux] 关于select单线程实现并发问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-09-04 19:42 |只看该作者 |倒序浏览
本帖最后由 wander__漫游世界 于 2014-09-04 19:43 编辑

学习select实现单线程实现并发,在网上找到两种版本。
一种能够实现并发通信,一种只有一个客户段能够通信,十分不解。下面上源码,求帮忙分析一下。
第一种能实现并发:
  1. /*
  2.         Thu Sep 4,4:12PM,2014
  3.         @wander
  4.         email:czpwander@gmail.com       
  5.         process: select socket server
  6.         ps:单进程用select实现并发socket
  7.         网上有几种不同的实现方式,本程序是通过主进程实现监听socket,然后用select实现连接socket
  8.         select 程序中是每次有一个新连接,然后将连接描述符添加到select描述符中,也就是一个一个添加。
  9. * */

  10. #include <stdio.h>
  11. #include <sys/socket.h>
  12. #include <sys/types.h>
  13. #include <string.h>
  14. #include <netinet/in.h>
  15. #include <unistd.h>
  16. #include <stdlib.h>
  17. #include <sys/select.h>
  18. #include <sys/time.h>

  19. #define LEN 1024
  20. #define PORT 60606
  21. #define BACKLOG 2

  22. struct client{
  23.         int fd;               
  24.         struct sockaddr_in cli;
  25. };

  26. int main(void)
  27. {
  28.         struct client cl[FD_SETSIZE];//客户端数组
  29.         int connfd;
  30.         int i;
  31.         int maxfd,sockfd;
  32.         int maxi = -1;
  33.         int nready;
  34.         char buf[LEN];// 接收发送socket buf
  35.         int listenfd = socket(AF_INET,SOCK_STREAM,0);//创建监听socket
  36.         if(listenfd < 0){
  37.                 perror("socket error\n");       
  38.                 exit(-1);
  39.         }

  40.         int opt = SO_REUSEADDR;
  41.         setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));//设置解绑地址
  42.         struct sockaddr_in ser;
  43.         socklen_t ser_len = sizeof(ser);
  44.         ser.sin_family = AF_INET;
  45.         ser.sin_port = htons(PORT);
  46.         ser.sin_addr.s_addr = htonl(INADDR_ANY);
  47.        
  48.         int ret = bind(listenfd,(struct sockaddr*)&ser,ser_len);//bind
  49.         if(ret < 0){
  50.                 perror("bind error\n");
  51.                 exit(-1);
  52.         }
  53.        
  54.         ret = listen(listenfd,BACKLOG); //listen
  55.         if(ret < 0){
  56.                 perror("listen error\n");       
  57.                 exit(-1);
  58.         }
  59.        
  60.         //select
  61.         maxfd = listenfd;
  62.         for(i = 0;i < FD_SETSIZE;i ++){
  63.                 cl[i].fd = -1;
  64.         }
  65.         fd_set read_fds;
  66.         fd_set r_fds;
  67.         FD_ZERO(&read_fds);
  68.         FD_ZERO(&r_fds);
  69.         FD_SET(listenfd,&read_fds);

  70.         while(1){
  71.                 memset(buf,0,LEN);       
  72.                 struct sockaddr_in addr;
  73.                 r_fds = read_fds;//将select描述符集,赋值给r_fds
  74.                 socklen_t addr_len = sizeof(addr);
  75.                 bzero(&addr,addr_len);

  76.                 nready = select(maxfd+1,&r_fds,NULL,NULL,NULL);//  select
  77.                 if(ret < 0){
  78.                         perror("select error\n");
  79.                         exit(-1);
  80.                 }
  81.                 if(FD_ISSET(listenfd,&r_fds)){// 判断是否有监听socket
  82.                         printf("get new connect\n");
  83.                         connfd = accept(listenfd,(struct sockaddr *)&addr,&addr_len);// accept socket
  84.                         if(connfd < 0){
  85.                                 perror("accept error\n");
  86.                                 exit(-1);
  87.                         }
  88.                         printf("accept ok\n");       
  89.                        
  90.                         //将得到客户端地址 连接socketfd
  91.                         for(i = 0;i < FD_SETSIZE;i ++){
  92.                                 if(cl[i].fd < 0){
  93.                                         cl[i].fd = connfd;
  94.                                         cl[i].cli = addr;
  95.                                         printf("got connect from %s\n",inet_ntoa(cl[i].cli.sin_addr));
  96.                                         //printf("got connect from client %d\n",i);
  97.                                         break;
  98.                                 }
  99.                         }
  100.                         if(i == FD_SETSIZE){// 连接达到最大值
  101.                                 printf("too many client connect\n");       
  102.                                 continue;
  103.                         }
  104.                         FD_SET(connfd,&read_fds);//将连接sockefd添加到select描述符集中
  105.                         if(connfd > maxfd)// 判断最大
  106.                                 maxfd = connfd;
  107.                         if(i > maxi){// 判断最大连接数
  108.                                 maxi = i;
  109.                                 printf("maxi:%d\n",maxi);
  110.                         }
  111.                         if(--nready <= 0)
  112.                                 continue;//如果没有连接,就继续循环
  113.                 }

  114.                 for(i = 0;i <= maxi;i ++){// 判断是否有数据发送
  115.                         if((sockfd = cl[i].fd) < 0)        //如果小于0,表示没有客户端连接
  116.                                 continue;
  117.                         if(FD_ISSET(sockfd,&r_fds)){
  118.                                 printf("get the data\n");
  119.                                 int r_size = recv(sockfd,buf,LEN,0);//接收数据
  120.                                 if(r_size == 0){
  121.                                         close(sockfd);
  122.                                         printf("close the %d client\n",i);
  123.                                 }
  124.                                 else if(r_size > 0){
  125.                                         printf("the client %d:",i);
  126.                                         printf("%s \n",buf);
  127.                                 }
  128.                                 if(--nready <= 0)
  129.                                         break;
  130.                         }
  131.                 }
  132.         }
  133.        
  134.         close(connfd);
  135.         close(listenfd);
  136. }



















复制代码
第二种,网上很多是第二种,但是能够建立连接,但是不能实现并发通信
  1. /* 实现功能:通过select处理多个socket
  2. * 监听一个端口,监听到有链接时,添加到select的w.
  3. */
  4. //#include "select.h"
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <sys/socket.h>
  8. #include <sys/select.h>
  9. #include <sys/time.h>
  10. #include <netinet/in.h>
  11. #include <string.h>

  12. typedef struct _CLIENT{
  13.     int fd;
  14.     struct sockaddr_in addr; /* client's address information */
  15. } CLIENT;

  16. #define MYPORT 60606

  17. //最多处理的connect
  18. #define BACKLOG 2

  19. //最多处理的connect
  20. CLIENT client[BACKLOG];

  21. //当前的连接数
  22. int currentClient = 0;

  23. //数据接受 buf
  24. #define REVLEN 10
  25. char recvBuf[REVLEN];
  26. //显示当前的connection
  27. void showClient();

  28. int main()
  29. {
  30.     int i, ret, sinSize;
  31.     int recvLen = 0;
  32.     fd_set readfds, writefds;
  33.     int sockListen, sockSvr, sockMax;
  34.     struct timeval timeout;
  35.     struct sockaddr_in server_addr;
  36.     struct sockaddr_in client_addr;

  37.         //将客户端端描述符置1
  38.     for(i=0; i<BACKLOG; i++)
  39.     {
  40.         client[i].fd = -1;
  41.     }

  42.     //socket
  43.     if((sockListen=socket(AF_INET, SOCK_STREAM, 0)) < 0)
  44.     {
  45.         printf("socket error\n");
  46.         return -1;
  47.     }

  48.         int opt = SO_REUSEADDR;
  49.         setsockopt(sockListen,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));
  50.     bzero(&server_addr, sizeof(server_addr));
  51.     server_addr.sin_family  =  AF_INET;
  52.     server_addr.sin_port = htons(MYPORT);
  53.     server_addr.sin_addr.s_addr  =  htonl(INADDR_ANY);

  54.     //bind
  55.     if(bind(sockListen, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
  56.     {
  57.         printf("bind error\n");
  58.         return -1;
  59.     }

  60.     //listen
  61.     if(listen(sockListen, 5) < 0)
  62.     {
  63.         printf("listen error\n");
  64.         return -1;
  65.     }

  66.     for(i=0; i<BACKLOG; i++)
  67.     {
  68.         client[i].fd = -1;
  69.     }

  70.     //select
  71.     while(1)
  72.     {
  73.         FD_ZERO(&readfds);
  74.         FD_SET(sockListen, &readfds);
  75.         sockMax = sockListen;
  76.    
  77.         //加入client
  78.         for(i=0; i<BACKLOG; i++)
  79.         {
  80.             if(client[i].fd >0)
  81.             {
  82.                 FD_SET(client[i].fd, &readfds);
  83.                 if(sockMax<client[i].fd)
  84.                     sockMax = client[i].fd;
  85.             }
  86.         }
  87.         
  88.         timeout.tv_sec=10;               
  89.         timeout.tv_usec=0;
  90.         //select
  91.         ret = select((int)sockMax+1, &readfds, NULL, NULL, &timeout);
  92.         if(ret < 0)
  93.         {
  94.             printf("select error\n");
  95.             break;
  96.         }
  97.         else if(ret == 0)
  98.         {
  99.             printf("timeout ...\n");
  100.             continue;
  101.         }
  102.         //printf("test111\n");
  103.    
  104.         //读取数据
  105.         for(i=0; i<BACKLOG; i++)
  106.         {
  107.             if(client[i].fd>0 && FD_ISSET(client[i].fd, &readfds))
  108.             {
  109.                 if(recvLen != REVLEN)
  110.                 {
  111.                     while(1)
  112.                     {
  113.                         //recv数据
  114.                         ret = recv(client[i].fd, (char *)recvBuf+recvLen, REVLEN-recvLen, 0);
  115.                         if(ret == 0)
  116.                         {
  117.                             client[i].fd = -1;
  118.                             recvLen = 0;
  119.                             break;
  120.                         }
  121.                         else if(ret < 0)
  122.                         {
  123.                             client[i].fd = -1;
  124.                             recvLen = 0;
  125.                             break;
  126.                         }
  127.                         //数据接受正常
  128.                         recvLen = recvLen+ret;
  129.                         if(recvLen<REVLEN)
  130.                         {
  131.                             continue;
  132.                         }
  133.                         else
  134.                         {
  135.                             //数据接受完毕
  136.                             printf("%s, buf = %s\n", inet_ntoa(client[i].addr.sin_addr) , recvBuf);
  137.                             //close(client[i].fd);
  138.                             //client[i].fd = -1;
  139.                             recvLen = 0;
  140.                             break;
  141.                         }
  142.                     }
  143.                 }
  144.             }
  145.         }
  146.    
  147.         //如果可读
  148.         if(FD_ISSET(sockListen, &readfds))
  149.         {
  150.             printf("isset\n");
  151.             sockSvr = accept(sockListen, NULL, NULL);//(struct sockaddr*)&client_addr
  152.         
  153.             if(sockSvr == -1)
  154.             {
  155.                 printf("accpet error\n");
  156.             }
  157.             else
  158.             {
  159.                 currentClient++;
  160.             }
  161.         
  162.             for(i=0; i<BACKLOG; i++)
  163.             {
  164.                 if(client[i].fd < 0)
  165.                 {
  166.                     client[i].fd = sockSvr;
  167.                     client[i].addr = client_addr;
  168.                     printf("You got a connection from %s \n",inet_ntoa(client[i].addr.sin_addr) );
  169.                     break;
  170.                 }
  171.             }
  172.             //close(sockListen);
  173.         }
  174.     }

  175.     printf("test\n");
  176.     return 0;
  177. }

  178. //显示当前的connection
  179. void showClient()
  180. {
  181.     int i;
  182.     printf("client count = %d\n", currentClient);

  183.     for(i=0; i<BACKLOG; i++)
  184.     {
  185.         printf("[%d] = %d", i, client[i].fd);
  186.     }
  187.     printf("\n");
  188. }
复制代码

论坛徽章:
0
2 [报告]
发表于 2014-09-05 09:23 |只看该作者
不想贴子沉了。顶一下。还是希望大神们能够看看,相互交流一下。
楼主不想只是将代码敲一遍,然后就没有了。楼主想深入进去好好学习,期望摆脱菜鸟,以后也能为linux社区做贡献

论坛徽章:
0
3 [报告]
发表于 2014-09-10 21:36 |只看该作者
没人知道,天啊,竟然没人知道

论坛徽章:
0
4 [报告]
发表于 2014-11-17 10:23 |只看该作者
回复 3# wander__漫游世界


    楼主研究的怎么样了,我也在学习select呢,你说的第一个例子的源码就是<<unix网络编程>>上的代码吧,第二个例子的源码我也不明白,我也想知道为什么,楼主有答案了么?
   楼主给解释下吧

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
5 [报告]
发表于 2014-11-30 09:54 |只看该作者
wander__漫游世界 发表于 2014-09-10 21:36
没人知道,天啊,竟然没人知道

第二个,为什么每次readfd都要accept呢?不是每个fd都要accept的。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP