首页 > 学院 > 操作系统 > 正文

IO复用_select函数

2024-06-28 13:19:54
字体:
来源:转载
供稿:网友
IO复用_select函数

select函数:

#include <sys/select.h>#include <time.h>#include <sys/types.h>#include <unistd.h>int select(int nfds,               fd_set*readfds,               fd_set*writefds,               fd_set*exceptfds,               struct timeval*timeout);

参数含义:

  • nfds:一个整型变量,它比所有文件描述符集合中的文件描述符的最大值大1。使用select的时候,必须计算最大值的文件描述符的值,将值通过nfds传入。
  • readfds:这个文件描述符集合监视文件集中的任何文件是否有数据可读,当select()返回的时候,readfds将清除其中不可读的文件描述符,只留下可读的文件描述符,即可以被recv()、read()等进行读操作。
  • writefds:这个文件描述符集合监视文件集中的任何文件是否有数据可写,当select()返回的时候,readfds将清除其中不可写的文件描述符,只留下可写的文件描述符,即可以被send()、write()等进行读操作。
  • exceptfds:这个文件集合将监视文件中的任何文件是否发生错误,其实,它还能用于监视带外数据OOB,带外数据使用MSG_OOB标志发送到套接字上。当select()返回时,readfds将清除其中的其他文件描述符,只留下可读的OOB数据。
  • timeout:设置在select()所监视的文件集合中的事件没有发生时,最长的等待时间,当超过这个时间时,函数返回。当超时返回为NULL时,表示阻塞操作,会一直等待,直到某个监视的文件集中的某个文件描述符符合返回条件。当timeout的值为0时,select()会立即返回。
  • sigmask:信号

返回值:

当返回大于0的正值:监视的文件集合中的文件描述符符合上述要求。

当等于0时:超时。

当为-1时:发生错误

struct timeval结构:

struct timeval{       time_t tv_sec;                //秒       long tv_usec;                 // 微秒,即1/1000000s}

另外,还有4个宏操作文件描述符的集合:

FD_ZERO():清理文件描述符集合;

FD_SET():向某个文件描述符集合中加入文件描述符;

FD_CLR():从某个文件描述符集合中取某个文件描述符;

FD_ISSET():测试某个文件描述符是否为某个集合中的一员。

注:文件描述符的集合存在最大的限制,其最大值为FD_SETSIZE,当超出最大值时,发生不可预料的事。同时,可以修改这个值,但是监视集合的效率会降低,是因为select()轮询是线性的。在这里,有个更加牛B的的函数,请查看epoll:epoll没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于2048, 一般来说这个数目和系统内存关系很大。最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率就会远远高于select和poll。在内存拷贝上,epoll在这点上使用了“共享内存”,这个内存拷贝也省略了。

附加:使用select()写的IO复用循环服务器模型的例子

  1 #include <sys/types.h>  2 #include <sys/socket.h>  3 #include <netinet/in.h>  4 #include <time.h>  5 #include <string.h>  6 #include <stdio.h>  7 #include <pthread.h>  8  #include <sys/select.h>  9 #define BUFFLEN 1024 10 #define SERVER_PORT 8888 11 #define BACKLOG 5 12 #define CLIENTNUM 1024/*最大支持客户端数量*/ 13  14 /*可连接客户端的文件描述符数组*/ 15 int connect_host[CLIENTNUM]; 16 int connect_number =  0; 17  18 //处理客户端请求函数 19 static void *handle_request(void *argv) 20 {     21     time_t now;        /*时间*/ 22     char buff[BUFFLEN];/*收发数据缓冲区*/ 23     int n = 0; 24      25     int maxfd = -1;/*最大侦听文件描述符*/ 26     fd_set scanfd;    /*侦听描述符集合*/ 27     struct   timeval   timeout;     /*超时*/ 28     timeout.tv_sec     =   1; /*   阻塞1秒后超时返回   */      29     timeout.tv_usec   =   0;      30      31     int i = 0; 32     int err  = -1; 33     for(;;) 34     {    35         /*最大文件描述符值初始化为-1*/         36         maxfd = -1; 37         FD_ZERO(&scanfd);/*清零文件描述符集合*/ 38         for(i=0;i<CLIENTNUM;i++)/*将文件描述符放入集合*/ 39         { 40             if(connect_host[i] != -1)/*合法的文件描述符*/ 41             { 42                 FD_SET(connect_host[i], &scanfd);/*放入集合*/ 43                 if(maxfd <     connect_host[i])/*更新最大文件描述符值*/ 44                 { 45                     maxfd = connect_host[i]; 46                 } 47             } 48         } 49         /*select等待*/ 50         err = select(maxfd + 1, &scanfd, NULL, NULL, &timeout) ;         51         switch(err) 52         { 53             case 0:/*超时*/ 54                 break; 55             case -1:/*错误发生*/ 56                 break; 57             default:/*有可读套接字文件描述符*/ 58                 if(connect_number<=0) 59                     break; 60                 for(i = 0;i<CLIENTNUM;i++) 61                 { 62                     /*查找激活的文件描述符*/ 63                     if(connect_host[i] != -1) 64                     if(FD_ISSET(connect_host[i],&scanfd))    65                     {   66                         memset(buff, 0, BUFFLEN);/*清零*/ 67                         n = recv(connect_host[i], buff, BUFFLEN,0);/*接收发送方数据*/ 68                         if(n > 0 && !strncmp(buff, "TIME", 4))/*判断是否合法接收数据*/ 69                         { 70                             memset(buff, 0, BUFFLEN);/*清零*/ 71                             now = time(NULL);/*当前时间*/ 72                             sPRintf(buff, "%24s/r/n",ctime(&now));/*将时间拷贝入缓冲区*/ 73                             send(connect_host[i], buff, strlen(buff),0);/*发送数据*/ 74                         } 75                         /*更新文件描述符在数组中的值*/ 76                         connect_host[i] = -1; 77                         connect_number --;    /*客户端计数器减1*/     78                         /*关闭客户端*/ 79                         close(connect_host[i]);                             80                     }  81                 } 82                 break;      83         }           84     }  85      86     return NULL; 87 } 88  89 //处理客户端连接函数 90 static void *handle_connect(void *argv) 91 {     92     int s_s = *((int*)argv) ;/*获得服务器侦听套接字文件描述符*/ 93     int s_c = -1;/*连接客户端文件描述符*/ 94     struct sockaddr_in from; 95     int len = sizeof(from); 96     /*接收客户端连接*/ 97     for(;;) 98     { 99         int i = 0;100         int s_c = accept(s_s, (struct sockaddr*)&from, &len);/*接收客户端的请求*/101         printf("a client connect, from:%s/n",inet_ntoa(from.sin_addr));102         /*查找合适位置,将客户端的文件描述符放入*/                103         for(i=0;i<CLIENTNUM;i++)104         {105             if(connect_host[i] == -1)/*找到*/106             {107                 /*放入*/108                 connect_host[i]= s_c;109                 110                 /*客户端计数器加1*/111                 connect_number ++;112                 /*继续轮询等待客户端连接*/113                 break;                        114             }    115         }        116     }    117     return NULL;118 }119 120 int main(int argc, char *argv[])121 {122     int s_s;    /*服务器套接字文件描述符*/123     struct sockaddr_in local;    /*本地地址*/    124     int i = 0;125     memset(connect_host, -1, CLIENTNUM);126     127     /*建立TCP套接字*/128     s_s = socket(AF_INET, SOCK_STREAM, 0);129     130     /*初始化地址接哦股*/131     memset(&local, 0, sizeof(local));/*清零*/132     local.sin_family = AF_INET;/*AF_INET协议族*/133     local.sin_addr.s_addr = htonl(INADDR_ANY);/*任意本地地址*/134     local.sin_port = htons(SERVER_PORT);/*服务器端口*/135     136     /*将套接字文件描述符绑定到本地地址和端口*/137     int err = bind(s_s, (struct sockaddr*)&local, sizeof(local));138     err = listen(s_s, BACKLOG);/*侦听*/139     140     pthread_t  thread_do[2];/*线程ID*/141     /*创建线程处理客户端连接*/142     pthread_create(&thread_do[0],/*线程ID*/143                     NULL,/*属性*/144                     handle_connect,/*线程回调函数*/145                     (void*)&s_s);        /*线程参数*/146     /*创建线程处理客户端请求*/                    147     pthread_create(&thread_do[1],/*线程ID*/148                     NULL,/*属性*/149                     handle_request,/*线程回调函数*/150                     NULL);        /*线程参数*/151     /*等待线程结束*/152     for(i=0;i<2;i++)153         pthread_join(thread_do[i], NULL);154     155     close(s_s);156     157     return 0;        158 }

作者:orange1438出处:http://www.CUOXin.com/orange1438/本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表