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/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。