poll 與 select 很類似,都是對描述符進行遍歷,查看是否有描述符就緒。如果有就返回就緒文件描述符的個數將。poll 函數如下:
#include <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout)
第一個參數指向結構數組第一個元素的指針,每個數組都是一個 pollfd 結構,用於指定測試某個給定描述符 fd 的條件。
struct pollfd
{
int fd;
short events;//關心 fd 上發生的事件
short revents;//fd 實際上上發生的事件
}
要測試的條件由 events 成員指定,函數在相應的 revents 成員中返回該描述符的狀態(每個描述符都有兩個變量,一個為調用值,另一個為返回結果,從而避免值-結果參數)這兩個成員中的每一個都由指定某個特定條件的一位或多位組合而成。下標列出指定 events 標志以及測試 revents 標志的一些常值。
----------------------------+---------------------------------------+---------------------------------------+----------------------------------------+
常量 | 能做為 events 的輸入嗎? | 能作為revents 的結果嗎? | 說明 |
----------------------------+---------------------------------------+---------------------------------------+----------------------------------------+
POLLIN | 能 | 能 | 普通或者優先級帶數據可讀 |
POLLRDNORM | 能 | 能 | 普通數據可讀 |
POLLRDBAND | 能 | 能 | 優先級帶數據可讀 |
POLLPRI | 能 | 能 | 高優先級數據可讀 |
----------------------------+---------------------------------------+---------------------------------------+----------------------------------------+
POLLOUT | 能 | 能 | 普通數據可寫 |
POLLWRNORM | 能 | 能 | 普通數據可寫 |
POLLWRBAND | 能 | 能 | 優先級帶數據可寫 |
----------------------------+---------------------------------------+--------------------------------------+-----------------------------------------+
POLLERR | | 能 | 發生錯誤 |
POLLHUP | | 能 | 發生掛起 |
POLLNVAL | | 能 | 描述字不是一個打開的文件 |
-----------------------------+--------------------------------------+---------------------------------------+----------------------------------------+
第二個參數 nfds 制定數組中元素個數。第三個參數指定 poll 函數返回前等待多長時間。 INFTIM 表示永遠等待, 0 代表立即返回, > 0 等待指定數目的秒數。
1 #include <sys/socket.h> 2 #include <netinet/in.h> 3 #include <stdio.h> 4 #include <error.h> 5 #include <errno.h> 6 #include <unistd.h> 7 #include <string.h> 8 #include <stdlib.h> 9 #include <sys/wait.h> 10 #include <limits.h> 11 #include <poll.h> 12 #include <sys/stropts.h> 13 #include <signal.h> 14 #define MAXLINE 5 15 #define OPEN_MAX 1024 16 #define SA struct sockaddr 17 18 19 int main() 20 { 21 int listenfd, connfd, sockfd, i, maxi; 22 int nready; 23 socklen_t clilen; 24 ssize_t n; 25 char buf[MAXLINE]; 26 struct pollfd client[OPEN_MAX]; 27 struct sockaddr_in servaddr, cliaddr; 28 //創建監聽套接字 29 if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 30 { 31 printf("socket() error!"); 32 exit(0); 33 } 34 //先要對協議地址進行清零 35 bzero(&servaddr,sizeof(servaddr)); 36 //設置為 IPv4 or IPv6 37 servaddr.sin_family = AF_INET; 38 //綁定本地端口號 39 servaddr.sin_port = htons(9805); 40 //任何一個 IP 地址,讓內核自行選擇 41 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 42 //綁定套接口到本地協議地址 43 if(bind(listenfd, (SA *) &servaddr,sizeof(servaddr)) < 0) 44 { 45 printf("bind() error!"); 46 exit(0); 47 } 48 //服務器開始監聽 49 if(listen(listenfd,5) < 0) 50 { 51 printf("listen() error!"); 52 exit(0); 53 } 54 client[0].fd = listenfd; 55 client[0].events = POLLRDNORM;//關心監聽套機字的讀事件 56 for(i = 1; i < OPEN_MAX; ++i) 57 { 58 client[i].fd = -1; 59 } 60 maxi = 0; 61 for(;;) 62 { 63 nready = poll(client, maxi + 1, -1); 64 if(client[0].revents & POLLRDNORM) 65 { 66 clilen = sizeof(cliaddr); 67 //accept 的后面兩個參數都是值-結果參數,他們的保留的遠程連接電腦的信息,如果不管新遠程連接電腦的信息,可以將這兩個參數設置為 NULL 68 connfd = accept(listenfd, (SA *) &cliaddr, &clilen); 69 if(connfd < 0) 70 { 71 continue; 72 } 73 for(i = 1; i < OPEN_MAX; ++i) 74 { 75 if(client[i].fd < 0) 76 client[i].fd = connfd; 77 break; 78 } 79 if(i == OPEN_MAX) 80 { 81 printf("too many clients"); 82 exit(0); 83 } 84 client[i].events = POLLRDNORM; 85 if(i > maxi) 86 { 87 maxi = i; 88 } 89 if(--nready <=0 ) 90 continue; 91 } 92 for(i = 1; i < OPEN_MAX; ++i) 93 { 94 if((sockfd = client[i].fd) < 0) 95 { 96 continue; 97 } 98 if(client[i].revents & POLLRDNORM | POLLERR) 99 { 100 if((n = read(sockfd, buf, MAXLINE)) < 0) 101 { 102 if(errno == ECONNRESET) 103 { 104 close(sockfd); 105 client[i].fd = -1; 106 } 107 else 108 { 109 printf("read error!\n"); 110 } 111 } 112 else if(n == 0) 113 { 114 close(sockfd); 115 client[i].fd = -1; 116 } 117 else 118 { 119 write(sockfd, buf, n); 120 } 121 if(--nready <= 0) 122 break; 123 } 124 } 125 } 126 }
配合 linux 下 select 編程 就是一個完整的客戶端/服務器端代碼了,運行結果截圖如下:
客戶端:
服務器端: