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);
參數含義:
返回值:
當返回大于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/本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
新聞熱點
疑難解答