poll數組
1、poll()函數:服務器
這個函數是某些Unix系統提供的用於執行與select()函數同等功能的函數,自認爲poll和select大同小異,下面是這個函數的聲明:
socket
#include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout);
參數:函數
1.第一個參數:一個結構數組,struct pollfd:測試
fds:是一個struct pollfd結構類型的數組,每一個數組元素都是一個pollfd結構,用於指定測試某個給定描述字fd的條件。存放須要檢測其狀態的Socket描述符;每當調用這個函數以後,系統不會清空這個數組,操做起來比較方便;特別是對於socket鏈接比較多的狀況下,在必定程度上能夠提升處理的效率;這一點與select()函數不一樣,調用select()函數以後,select()函數會清空它所檢測的socket描述符集合,致使每次調用select()以前都必須把socket描述符從新加入到待檢測的集合中;所以,select()函數適合於只檢測一個socket描述符的狀況,而poll()函數適合於大量socket描述符的狀況;
spa
結構以下:code
struct pollfd{ int fd; //文件描述符 short events; //請求的事件 short revents; //返回的事件 };
events和revents是經過對錶明各類事件的標誌進行邏輯或運算構建而成的。events包括要監視的事件(就是我須要關注的時間,是讀?是寫?仍是出錯?),poll用已經發生的事件填充revents。poll函數經過在revents中設置標誌肌膚POLLHUP、POLLERR和POLLNVAL來反映相關條件的存在。不須要在events中對於這些標誌符相關的比特位進行設置。若是fd小於0, 則events字段被忽略,而revents被置爲0.標準中沒有說明如何處理文件結束。文件結束能夠經過revents的標識符POLLHUN或返回0字節的常規讀操做來傳達。即便POLLIN或POLLRDNORM指出還有數據要讀,POLLHUP也可能會被設置。所以,應該在錯誤檢驗以前處理正常的讀操做。blog
poll函數的事件標誌符值:接口
常量 | 說明 |
POLLIN | 普通或優先級帶數據可讀 |
POLLRDNORM | 普通數據可讀 |
POLLRDBAND | 優先級帶數據可讀 |
POLLPRI | 高優先級數據可讀 |
POLLOUT | 普通數據可寫 |
POLLWRNORM | 普通數據可寫 |
POLLWRBAND | 優先級帶數據可寫 |
POLLERR | 發生錯誤 |
POLLHUP | 發生掛起 |
POLLNVAL | 描述字不是一個打開的文件 |
注意:進程
1)後三個只能做爲描述字的返回結果存儲在revents中,而不能做爲測試條件用於events中。
2)第二個參數nfds:要監視的描述符的數目。
3)最後一個參數timeout:是一個用毫秒表示的時間,是指定poll在返回前沒有接收事件時應該等待的時間。若是 它的值爲-1,poll就永遠都不會超時。若是整數值爲32個比特,那麼最大的超時週期大約是30分鐘。
timeout值 | 說明 |
INFTIM | 永遠等待 |
0 | 當即返回,不阻塞進程 |
>0 | 等待指定數目的毫秒數 |
對socket描述符fd上的讀、寫、異常事件感興趣,就能夠這樣作:
struct pollfd fds; fds[index].events=POLLIN | POLLOUT | POLLERR;
struct pollfd fds; //檢測可讀TCP鏈接請求: if((fds[nIndex].revents & POLLIN) == POLLIN) { <span style="white-space:pre"> </span>//接收數據,調用accept()接收鏈接請求 } //檢測可寫: if((fds[nIndex].revents & POLLOUT) == POLLOUT) { <span style="white-space:pre"> </span>//發送數據 } //檢測異常: if((fds[nIndex].revents & POLLERR) == POLLERR) { <span style="white-space:pre"> </span>//異常處理 }2、實例TCP服務器的服務器程序
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netdb.h> #include <string.h> #include <errno.h> #include <poll.h> //for poll #define LISTENQ 1024 #define MAXLINE 1024 #define OPEN_MAX 50000 #ifndef INFTIM #define INFTIM -1 #endif int start_up(char* ip,int port) //建立一個套接字,綁定,檢測服務器 { //sock //1.建立套接字 int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("sock"); exit(0); } //2.填充本地 sockaddr_in 結構體(設置本地的IP地址和端口) struct sockaddr_in local; local.sin_port=htons(port); local.sin_family=AF_INET; local.sin_addr.s_addr=inet_addr(ip); //3.bind()綁定 if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind"); exit(1); } //4.listen()監聽 檢測服務器 if(listen(sock,back_log)<0) { perror("sock"); exit(1); } return sock; //這樣的套接字返回 } int main(int argc, char *argv[]) { int i, maxi, connfd, sockfd; int nready; ssize_t n; socklen_t clilen; struct sockaddr_in servaddr; socklen_t len=sizeof(servaddr); char buf[BUFSIZ]; struct pollfd client[OPEN_MAX]; // 用於poll函數第一個參數的數組,存放每次的文件描述符個數 if( argc != 3 ) { printf("Please input %s <hostname>\n", argv[0]); exit(2); } int listenfd=start_up(argv[1],argv[2]); //建立一個綁定了本地 ip 和端口號的套接字描述符 client[0].fd = listenfd; //將數組中的第一個元素設置成監聽描述字 client[0].events = POLLIN; //將測試條件設置成普通或優先級帶數據可讀(感興趣的事件讀、寫、出錯),此處書中爲POLLRDNORM,*/ client[0].revents = 0; //真正發生的事件 for(i = 1;i < OPEN_MAX; ++i) //數組中的其它元素將暫時設置成不可用 { client[i].fd = -1; } maxi = 0; while(1) { nready = poll(client, maxi+1,INFTIM); //將進程阻塞在poll上 if( client[0].revents & POLLIN) //先測試監聽描述字 { connfd = accept(listenfd,(struct sockaddr*)&servaddr, &clilen); for(i = 1; i < OPEN_MAX; ++i) { if( client[i].fd < 0 ) { client[i].fd = connfd; //將新鏈接加入到測試數組中 client[i].events = POLLIN; //POLLRDNORM; 測試條件普通數據可讀 break; } if( i == OPEN_MAX ) { printf("too many clients"); //鏈接的客戶端太多了,都達到最大值了 exit(1); } if( i > maxi ) maxi = i; //maxi記錄的是數組元素的個數 if( --nready <= 0 ) continue; //若是沒有可讀的描述符了,就從新監聽鏈接 } } for(i = 1; i <= maxi; i++) //測試除監聽描述字之後的其它鏈接描述字 { if( (sockfd = client[i].fd) < 0) //若是當前描述字不可用,就測試下一個 continue; if(client[i].revents & (POLLIN | POLLERR))//若是當前描述字返回的是普通數據可讀或出錯條件 { if( (n = read(sockfd, buf, MAXLINE)) < 0) //從套接口中讀數據 { if( errno == ECONNRESET) //若是鏈接斷開,就關閉鏈接,並設當前描述符不可用 { close(sockfd); client[i].fd = -1; } else perror("read error"); } else if(n == 0) //若是數據讀取完畢,關閉鏈接,設置當前描述符不可用 { close(sockfd); client[i].fd = -1; } else write(sockfd, buf, n); //打印數據 if(--nready <= 0) break; } } } exit(0); }賜教!