select函数

前面两篇介绍用进程方式和线程方式实现并发服务,其实里面调用send,read,accept函数都会导致阻塞,而linux的select函数可以使我们在程序中同时监听多个文件描述符的读写状态。程序会停在select这里等待,直到被监视的文件描述符有某一个或多个发生了状态改变。select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组, 每一个数组元素都能与一打开的文件描述符(不管是Socket描述符,还是其他 文件或命名管道或设备描述符)建立联系, 当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一Socket或文件可读

下面我们用select函数实现单进程单线程的并发服务,客户端代码同多进程版本,服务器端代码如下

[cpp]  view plain copy
  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<string.h>  
  4. #include<sys/types.h>  
  5. #include<sys/socket.h>  
  6. #include<netinet/in.h>  
  7. #include<arpa/inet.h>  
  8.   
  9. #define MAX_LISTEN 5  
  10. #define PORT 1987  
  11. #define IP "127.0.0.1"  
  12.   
  13. int main()  
  14. {  
  15.     int conn_fd;  
  16.     int sock_fd = socket(AF_INET,SOCK_STREAM,0);  
  17.     if (sock_fd < 0) {  
  18.         perror("create socket failed");  
  19.         exit(1);  
  20.     }  
  21.   
  22.     struct sockaddr_in addr_client;  
  23.     int client_size = sizeof(struct sockaddr_in);  
  24.   
  25.     struct sockaddr_in addr_serv;  
  26.     memset(&addr_serv, 0, sizeof(addr_serv));  
  27.     addr_serv.sin_family = AF_INET;  
  28.     addr_serv.sin_port = htons(PORT);  
  29.     addr_serv.sin_addr.s_addr = inet_addr(IP);  
  30.   
  31.     if (bind(sock_fd,(struct sockaddr *)&addr_serv,sizeof(struct sockaddr_in)) < 0) {  
  32.         perror("bind error");  
  33.         exit(1);  
  34.     }  
  35.   
  36.     if (listen(sock_fd,MAX_LISTEN) < 0) {  
  37.         perror("listen failed");  
  38.         exit(1);  
  39.     }  
  40.   
  41.     int recv_num;  
  42.     int send_num;  
  43.     char recv_buf[100];  
  44.     char send_buf[100];   
  45.   
  46.     //用一个数组记录描述符的状态  
  47.     int i, ready, max_fd;  
  48.     int client[FD_SETSIZE];  
  49.     for (i = 0;i < FD_SETSIZE;i ++) {  
  50.         client[i] = -1;  
  51.     }  
  52.   
  53.     fd_set readset;  
  54.     max_fd = sock_fd;  
  55.   
  56.     //最大可用描述符的个数,一般受操作系统内核的设置影响,我的环境下这个值是1024  
  57.     printf("max fd num %d\n",FD_SETSIZE);  
  58.   
  59.     while (1) {  
  60.   
  61.         //重置监听的描述符  
  62.         FD_ZERO(&readset);  
  63.         FD_SET(sock_fd,&readset);  
  64.         for (i = 0;i < FD_SETSIZE;i ++) {  
  65.             if (client[i] == 1) {  
  66.                 FD_SET(i, &readset);  
  67.             }  
  68.         }  
  69.   
  70.         //开始监听描述符,是异步的,不会阻塞  
  71.         ready = select(max_fd+1, &readset, NULL, NULL, NULL);  
  72.           
  73.         //可用描述符如果是创建连接描述符,则创建一个新的连接  
  74.         if (FD_ISSET(sock_fd, &readset)) {  
  75.             conn_fd = accept(sock_fd, (struct sockaddr *)&addr_client, &client_size);  
  76.             if (conn_fd < 0) {  
  77.                 perror("accept failed");  
  78.                 exit(1);  
  79.             }  
  80.   
  81.             FD_SET(conn_fd, &readset);  
  82.             FD_CLR(sock_fd, &readset);  
  83.   
  84.             if (conn_fd > max_fd) {  
  85.                 max_fd = conn_fd;  
  86.             }  
  87.             client[conn_fd] = 1;      
  88.         }  
  89.           
  90.         //检查所有的描述符,查看可读的是哪个,针对它进行IO读写  
  91.         for (i = 0; i < FD_SETSIZE; i ++) {  
  92.             if (FD_ISSET(i, &readset)) {  
  93.   
  94.                 recv_num = recv(i, recv_buf, sizeof(recv_buf), 0);  
  95.                 if (recv_num <= 0) {  
  96.                     FD_CLR(i, &readset);  
  97.                     client[i] = -1;  
  98.                 }  
  99.                 recv_buf[recv_num] = '\0';  
  100.                 memset(send_buf,0,sizeof(send_buf));  
  101.                 sprintf(send_buf, "server proc got %d bytes\n", recv_num);  
  102.                 send_num = send(i, send_buf, strlen(send_buf), 0);  
  103.                 if (send_num <= 0) {  
  104.                     FD_CLR(i, &readset);  
  105.                     client[i] = -1;  
  106.                 }  
  107.             }  
  108.         }  
  109.     }  
  110.           
  111.     close(sock_fd);  
  112.     return 0;  
  113. }  
程序的效果和单进程,单线程都是一样的,不过我上面存在两个问题还没解决,我主要也是简单了解select的使用方法,没有做过多钻研。

1,每次select之前我都重新设置了一遍需要监听的描述符,要是能把这个提取到while循环外就好了(不过放出去程序就会错误,未查清原因)

2,我没有引入可写的fd_set,其实应该将文件描述符的可写状态也作为监听变量传给select函数,不过上面的示例代码可用即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值