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.cnblogs.com/orange1438/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2015-07-02 01:27  橙&子  阅读(1037)  评论(0编辑  收藏  举报