一、基本知識數組
poll的機制與select相似,與select在本質上沒有多大差異,管理多個描述符也是進行輪詢,根據描述符的狀態進行處理,可是poll沒有最大文件描述符數量的限制。poll和select一樣存在一個缺點就是,包含大量文件描述符的數組被總體複製於用戶態和內核的地址空間之間,而不論這些文件描述符是否就緒,它的開銷隨着文件描述符數量的增長而線性增大。服務器
二、poll函數less
函數格式以下所示:socket
# include <poll.h> int poll ( struct pollfd * fds, unsigned int nfds, int timeout);
pollfd結構體定義以下:函數
struct pollfd {測試
int fd; /* 文件描述符 */
short events; /* 等待的事件 */
short revents; /* 實際發生了的事件 */
} ; spa
每個pollfd結構體指定了一個被監視的文件描述符,能夠傳遞多個結構體,指示poll()監視多個文件描述符。每一個結構體的events域是監視該文件描述符的事件掩碼,由用戶來設置這個域。revents域是文件描述符的操做結果事件掩碼,內核在調用返回時設置這個域。events域中請求的任何事件均可能在revents域中返回。合法的事件以下:指針
POLLIN 有數據可讀。code
POLLRDNORM 有普通數據可讀。server
POLLRDBAND 有優先數據可讀。
POLLPRI 有緊迫數據可讀。
POLLOUT 寫數據不會致使阻塞。
POLLWRNORM 寫普通數據不會致使阻塞。
POLLWRBAND 寫優先數據不會致使阻塞。
POLLMSGSIGPOLL 消息可用。
此外,revents域中還可能返回下列事件:
POLLER 指定的文件描述符發生錯誤。
POLLHUP 指定的文件描述符掛起事件。
POLLNVAL 指定的文件描述符非法。
這些事件在events域中無心義,由於它們在合適的時候老是會從revents中返回。
使用poll()和select()不同,你不須要顯式地請求異常狀況報告。
POLLIN | POLLPRI等價於select()的讀事件,POLLOUT |POLLWRBAND等價於select()的寫事件。POLLIN等價於POLLRDNORM |POLLRDBAND,而POLLOUT則等價於POLLWRNORM。例如,要同時監視一個文件描述符是否可讀和可寫,咱們能夠設置 events爲POLLIN |POLLOUT。在poll返回時,咱們能夠檢查revents中的標誌,對應於文件描述符請求的events結構體。若是POLLIN事件被設置,則文件描述符能夠被讀取而不阻塞。若是POLLOUT被設置,則文件描述符能夠寫入而不致使阻塞。這些標誌並非互斥的:它們可能被同時設置,表示這個文件描述符的讀取和寫入操做都會正常返回而不阻塞。
timeout參數指定等待的毫秒數,不管I/O是否準備好,poll都會返回。timeout指定爲負數值表示無限超時,使poll()一直掛起直到一個指定事件發生;timeout爲0指示poll調用當即返回並列出準備好I/O的文件描述符,但並不等待其它的事件。這種狀況下,poll()就像它的名字那樣,一旦選舉出來,當即返回。
返回值和錯誤代碼
成功時,poll()返回結構體中revents域不爲0的文件描述符個數;若是在超時前沒有任何事件發生,poll()返回0;失敗時,poll()返回-1,並設置errno爲下列值之一:
EBADF 一個或多個結構體中指定的文件描述符無效。
EFAULTfds 指針指向的地址超出進程的地址空間。
EINTR 請求的事件以前產生一個信號,調用能夠從新發起。
EINVALnfds 參數超出PLIMIT_NOFILE值。
ENOMEM 可用內存不足,沒法完成請求。
三、測出程序
編寫一個echo server程序,功能是客戶端向服務器發送信息,服務器接收輸出並原樣發送回給客戶端,客戶端接收到輸出到終端。
服務器端程序以下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <errno.h> 5 6 #include <netinet/in.h> 7 #include <sys/socket.h> 8 #include <poll.h> 9 #include <unistd.h> 10 #include <sys/types.h> 11 12 #define IPADDRESS "127.0.0.1" 13 #define PORT 8787 14 #define MAXLINE 1024 15 #define LISTENQ 5 16 #define OPEN_MAX 1000 17 #define INFTIM -1 18 19 //函數聲明 20 //建立套接字並進行綁定 21 static int socket_bind(const char* ip,int port); 22 //IO多路複用poll 23 static void do_poll(int listenfd); 24 //處理多個鏈接 25 static void handle_connection(struct pollfd *connfds,int num); 26 27 int main(int argc,char *argv[]) 28 { 29 int listenfd,connfd,sockfd; 30 struct sockaddr_in cliaddr; 31 socklen_t cliaddrlen; 32 listenfd = socket_bind(IPADDRESS,PORT); 33 listen(listenfd,LISTENQ); 34 do_poll(listenfd); 35 return 0; 36 } 37 38 static int socket_bind(const char* ip,int port) 39 { 40 int listenfd; 41 struct sockaddr_in servaddr; 42 listenfd = socket(AF_INET,SOCK_STREAM,0); 43 if (listenfd == -1) 44 { 45 perror("socket error:"); 46 exit(1); 47 } 48 bzero(&servaddr,sizeof(servaddr)); 49 servaddr.sin_family = AF_INET; 50 inet_pton(AF_INET,ip,&servaddr.sin_addr); 51 servaddr.sin_port = htons(port); 52 if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1) 53 { 54 perror("bind error: "); 55 exit(1); 56 } 57 return listenfd; 58 } 59 60 static void do_poll(int listenfd) 61 { 62 int connfd,sockfd; 63 struct sockaddr_in cliaddr; 64 socklen_t cliaddrlen; 65 struct pollfd clientfds[OPEN_MAX]; 66 int maxi; 67 int i; 68 int nready; 69 //添加監聽描述符 70 clientfds[0].fd = listenfd; 71 clientfds[0].events = POLLIN; 72 //初始化客戶鏈接描述符 73 for (i = 1;i < OPEN_MAX;i++) 74 clientfds[i].fd = -1; 75 maxi = 0; 76 //循環處理 77 for ( ; ; ) 78 { 79 //獲取可用描述符的個數 80 nready = poll(clientfds,maxi+1,INFTIM); 81 if (nready == -1) 82 { 83 perror("poll error:"); 84 exit(1); 85 } 86 //測試監聽描述符是否準備好 87 if (clientfds[0].revents & POLLIN) 88 { 89 cliaddrlen = sizeof(cliaddr); 90 //接受新的鏈接 91 if ((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1) 92 { 93 if (errno == EINTR) 94 continue; 95 else 96 { 97 perror("accept error:"); 98 exit(1); 99 } 100 } 101 fprintf(stdout,"accept a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port); 102 //將新的鏈接描述符添加到數組中 103 for (i = 1;i < OPEN_MAX;i++) 104 { 105 if (clientfds[i].fd < 0) 106 { 107 clientfds[i].fd = connfd; 108 break; 109 } 110 } 111 if (i == OPEN_MAX) 112 { 113 fprintf(stderr,"too many clients.\n"); 114 exit(1); 115 } 116 //將新的描述符添加到讀描述符集合中 117 clientfds[i].events = POLLIN; 118 //記錄客戶鏈接套接字的個數 119 maxi = (i > maxi ? i : maxi); 120 if (--nready <= 0) 121 continue; 122 } 123 //處理客戶鏈接 124 handle_connection(clientfds,maxi); 125 } 126 } 127 128 static void handle_connection(struct pollfd *connfds,int num) 129 { 130 int i,n; 131 char buf[MAXLINE]; 132 memset(buf,0,MAXLINE); 133 for (i = 1;i <= num;i++) 134 { 135 if (connfds[i].fd < 0) 136 continue; 137 //測試客戶描述符是否準備好 138 if (connfds[i].revents & POLLIN) 139 { 140 //接收客戶端發送的信息 141 n = read(connfds[i].fd,buf,MAXLINE); 142 if (n == 0) 143 { 144 close(connfds[i].fd); 145 connfds[i].fd = -1; 146 continue; 147 } 148 // printf("read msg is: "); 149 write(STDOUT_FILENO,buf,n); 150 //向客戶端發送buf 151 write(connfds[i].fd,buf,n); 152 } 153 } 154 }
客戶端代碼以下所示:
1 #include <netinet/in.h> 2 #include <sys/socket.h> 3 #include <stdio.h> 4 #include <string.h> 5 #include <stdlib.h> 6 #include <poll.h> 7 #include <time.h> 8 #include <unistd.h> 9 #include <sys/types.h> 10 11 #define MAXLINE 1024 12 #define IPADDRESS "127.0.0.1" 13 #define SERV_PORT 8787 14 15 #define max(a,b) (a > b) ? a : b 16 17 static void handle_connection(int sockfd); 18 19 int main(int argc,char *argv[]) 20 { 21 int sockfd; 22 struct sockaddr_in servaddr; 23 sockfd = socket(AF_INET,SOCK_STREAM,0); 24 bzero(&servaddr,sizeof(servaddr)); 25 servaddr.sin_family = AF_INET; 26 servaddr.sin_port = htons(SERV_PORT); 27 inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr); 28 connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)); 29 //處理鏈接描述符 30 handle_connection(sockfd); 31 return 0; 32 } 33 34 static void handle_connection(int sockfd) 35 { 36 char sendline[MAXLINE],recvline[MAXLINE]; 37 int maxfdp,stdineof; 38 struct pollfd pfds[2]; 39 int n; 40 //添加鏈接描述符 41 pfds[0].fd = sockfd; 42 pfds[0].events = POLLIN; 43 //添加標準輸入描述符 44 pfds[1].fd = STDIN_FILENO; 45 pfds[1].events = POLLIN; 46 for (; ;) 47 { 48 poll(pfds,2,-1); 49 if (pfds[0].revents & POLLIN) 50 { 51 n = read(sockfd,recvline,MAXLINE); 52 if (n == 0) 53 { 54 fprintf(stderr,"client: server is closed.\n"); 55 close(sockfd); 56 } 57 write(STDOUT_FILENO,recvline,n); 58 } 59 //測試標準輸入是否準備好 60 if (pfds[1].revents & POLLIN) 61 { 62 n = read(STDIN_FILENO,sendline,MAXLINE); 63 if (n == 0) 64 { 65 shutdown(sockfd,SHUT_WR); 66 continue; 67 } 68 write(sockfd,sendline,n); 69 } 70 } 71 }
四、程序測試結果
五、參考資料
http://blog.endlesscode.com/2010/03/27/select-poll-epoll-intro/
http://hi.baidu.com/xzf20082004/item/622fb01a1018c7f5746a846f