你的位置:首页 > 操作系统

[操作系统]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在这点上使用了“共享内存”,这个内存拷贝也省略了----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.cnblogs.com/orange1438/本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。