poll f服务器
1.poll監聽的文件描述符沒有最大數量的限制
2.poll對于select來說包含了一個pollfd結構,pollfd結構包含了要監視的event和發生的revent,而不像select那樣使用輸入輸出的傳遞方式。所以不需要每次監聽都初始化
poll缺點:
1.數量過大以后其效率也會線性下降。
2.poll和select一樣也是返回就緒事件的個數,需要遍歷文件描述符來判斷是那個事件已經就緒,當數量很大時,開銷也就很大。
3.select和poll都只能工作在低效的LT(水平觸發)模式
4.每次調用poll,都需要把pollfd數組從用戶態拷貝到內核態,這個開銷在fd很多時會很大
5.內核采用輪詢(遍歷pollfd數組)的方式來檢測就緒事件,這個開銷在fd很多時也很大
?int poll(struct pollfd *fds, nfds_t nfds, int timeout);
1
1
fds是一個pollfd的結構體數組。
struct pollfd {
? ? ? ? ? ? ? ?int ? fd; ? ? ? ? /* file descriptor */
? ? ? ? ? ? ? ?short events; ? ? /* requested events */
? ? ? ? ? ? ? ?short revents; ? ?/* returned events */
? ? ? ? ? ?};
1
2
3
4
5
6
1
2
3
4
5
6
這就是這個結構體數組每個元素。fd用來記錄對應的文件描述符,events用來表示poll所監聽的事件,這個由用戶來設置。revents用來表示返回的事件。revents是通過內核來進行操作修改。
這里提供了一些合法事件。
事件 說明
POLLIN 普通或優先級帶數據可讀
POLLRDNORM 普通數據可讀
POLLRDBAND 優先級帶數據可讀
POLLPRI 高優先級數據可讀
POLLOUT 普通數據可寫
POLLWRNORM 普通數據可寫
POLLWRBAND 優先級帶數據可寫
POLLERR 發生錯誤
POLLHUP 發生掛起
POLLNVAL 描述字不是一個打開的文件
后面的三個參數在events無意義,只能作為返回結果存儲在revents。
另外,這里需要說的,這些參數如何設置給events,這些宏相當于每一個占用一個比特位,我們可以去想一下位圖,所以,如果我們要進行設置兩個事件,就使用|操作,另外,當我們去查看事件是否發生的時候,這個時候我們可以使用revents&事件,如果事件發生了,那么結果大于1。這就是一個簡單的位運算的,相信你仔細想想就能夠理解。
第二個參數nfds,用來監視的文件描述符的數目。
第三個參數是timeout,用來設置超時時間。
參數 說明
-1 poll將永遠阻塞,等待知道某個時間發生
0 立即返回
大于0的值 設置正常時間值
返回值?
poll返回revents不為0的文件描述符的個數。?
失敗返回-1
總結
poll本質上和select沒有區別,它將用戶傳入的數組拷貝到內核空間,然后查詢每個fd對應的設備狀態,如果設備就緒則在設備等待隊列中加入一項并繼續遍歷,如果遍歷完所有fd后沒有發現就緒設備,則掛起當前進程,直到設備就緒或者主動超時,被喚醒后它又要再次遍歷fd。這個過程經歷了多次無謂的遍歷。
poll使用了events和revents分流的特點,這樣可以使得對關心事件只進行注冊一次。
poll基于鏈表進行存儲,沒有最大連接數的限制,只取決于內存大小。
poll還有一個特點是“水平觸發”,如果報告了fd后,沒有被處理,那么下次poll時會再次報告該fd。
poll的實現機制與select類似,其對應內核中的sys_poll,只不過poll向內核傳遞pollfd數組,然后對pollfd中的每個描述符進行poll,相比處理fdset來說,poll效率更高。poll返回后,需要對pollfd中的每個元素檢查其revents值,來得指事件是否發生。
poll的缺點
1、大量的fd的數組被整體復制于用戶態和內核地址空間之間,而不管這樣是不是有意義。
2、poll依然需要進行輪詢,所消耗的時間太多。
3、水平觸發,效率低
代碼:
[cpp] view plain copy
#include<stdio.h> ?
#include <sys/types.h> ? ? ? ? ?/* See NOTES */ ?
#include <sys/socket.h> ?
#include<stdlib.h> ?
#include<netinet/in.h> ?
#include<poll.h> ?
#include<string.h> ?
??
??
static void usage(const char *str) ?
{ ?
? ? printf("Usage:%s [serv_ip] [serv_port]\n",str); ?
} ?
??
static int startup(const char *ip,int port) ?
{ ?
? ? int sock = socket(AF_INET,SOCK_STREAM,0); ?
? ? if(sock < 0) ?
? ? { ?
? ? ? ? perror("socket"); ?
? ? ? ? exit(1); ?
? ? } ?
??
? ? int opt = 1; ?
? ? setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); ?
??
? ? struct sockaddr_in serv_addr; ?
? ? serv_addr.sin_family = AF_INET; ?
? ? serv_addr.sin_addr.s_addr = inet_addr(ip); ?
? ? serv_addr.sin_port = htons(port); ?
??
? ? int ret ?= bind(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)); ?
? ? if(ret < 0) ?
? ? { ?
? ? ? ? perror("bind"); ?
? ? ? ? exit(2); ?
? ? } ?
??
? ? ret = listen(sock,128); ?
? ? if(ret < 0) ?
? ? { ?
? ? ? ? perror("listen"); ?
? ? ? ? exit(3); ?
? ? } ?
? ? return sock; ?
} ?
??
// ? ? ? poll函數第一個參數類型。 ?
//struct pollfd { ?
// ?int ? fd; ? ? ? ? /* file descriptor */ ?
// ?short events; ? ? /* requested events */ ?
// ?short revents; ? ?/* returned events */ ?
//}; ?
// ?
??
int main(int argc ,char *argv[]) ?
{ ?
? ? if(argc != 3) ?
? ? { ?
? ? ? ? usage(argv[0]); ?
? ? ? ? return 1; ?
? ? } ?
??
? ? int sock = startup(argv[1],atoi(argv[2]) ); ?
? ? struct pollfd peerfd[1024]; //當有就緒事件發生的話會寫到這個數組里面。 ?
??
? ? peerfd[0].fd = sock; ?//首先監聽連接套接字。監聽它的讀事件。 ?
? ? peerfd[0].events = POLLIN; ?
??
? ? int timeout = -1; ?//表示阻塞式等待。 ?
? ? int i = 1; ?
??
? ? for(; i < 1024; ++i) ?
? ? { ?
? ? ? ? peerfd[i].fd = -1; ? ?//表示這個位置么有被占用。 ?
? ? } ?
??
? ? while(1) ?
? ? { ? ? ? ? ? ? ? //int poll(struct pollfd *fds, nfds_t nfds, int timeout); ?
? ? ? ? int ret = 0; ?
? ? ? ? switch(ret = poll(peerfd,1024,timeout)) ?
? ? ? ? { ?
? ? ? ? ? ? case 0: ?
? ? ? ? ? ? ? ? printf("timeout...\n"); ?
? ? ? ? ? ? ? ? break; ?
? ? ? ? ? ? case -1: ?
? ? ? ? ? ? ? ? perror("poll"); ?
? ? ? ? ? ? ? ? break; ?
? ? ? ? ? ? default: ?
? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? int i = 0; ?
? ? ? ? ? ? ? ? ? ? for(i = 0; i < 1024; ++i)//遍歷數組查看有哪些就緒事件發生了。 ?
? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? if(i == 0 && (peerfd[i].revents & POLLIN) ?)//有新的連接請求。 ?
? ? ? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? struct sockaddr_in client; ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? socklen_t len = sizeof(client); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? int new_sock = accept(sock,(struct sockaddr*)&client,&len); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? if(new_sock < 0) ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? perror("accept"); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue; ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("get a new client\n"); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? int j = 1; ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? for(; j < 1024; ++j) ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(peerfd[j].fd < 0) ?//找最小的未被使用的位置。 ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peerfd[j].fd = new_sock; ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peerfd[j].events = POLLIN; ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break; ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? } ?
??
? ? ? ? ? ? ? ? ? ? ? ? ? ? if(j == 1024 ) ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("too many client...\n"); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? close(new_sock); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? ? ? ? ? } //if ?
? ? ? ? ? ? ? ? ? ? ? ? else if(i != 0) ?
? ? ? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? if(peerfd[i].revents & POLLIN) //客戶端有讀事件發生。 ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? char buf[1024]; ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ssize_t s = ?read(peerfd[i].fd,buf,sizeof(buf) - 1); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(s > 0) ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? buf[s] = 0; ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("clinet say:%s\n",buf); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peerfd[i].events = POLLOUT; //讀完后監聽寫事件。 ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? else if(s <= 0) ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? close(peerfd[i].fd); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peerfd[i].fd = -1; ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? else if(peerfd[i].revents & POLLOUT)//寫事件就緒。 ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* 客戶端寫事件發生 */ ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* 使用瀏覽器測試,寫回到客戶端,瀏覽器會解析字符串,顯示 Hellp Epoll! */ ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const char* msg = "HTTP/1.1 200 OK\r\n\r\n<html><br/><h1>Hello poll!</h1></html>"; ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? write(peerfd[i].fd, msg, strlen(msg)); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //寫完后關閉套接字。 ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? close(peerfd[i].fd); ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? peerfd[i].fd = -1; ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? } ?
??
? ? ? ? ? ? ? ? ? ? ? ? } //else if ?
??
? ? ? ? ? ? ? ? ? ? } //for ?
??
? ? ? ? ? ? ? ? } //default ?
??
? ? ? ? } //switch() ?
? ? } //while(1) ?
??
? ? return 0; ?
} ?
總結