poll函數
在上文unix下網絡編程之I/O復用(二)中已經介紹了select函數的相關使用,本文將介紹另一個常用的I/O復用函數poll。poll提供的功能與select類似,不過在處理流設備時,它能夠提供額外的信息。
poll函數原型:
#include<poll.h> int poll (struct pollfd * fdarray , unsigned long nfds , int timeout); //返回:就需描述字的個數,0——超時,-1——出錯
第一個參數是指向一個結構數組第一個元素的指針,每個數組元素都是一個pollfd結構。如下:
struct pollfd { int fd; //descriptor to check short events; //events of interest on fd ` short revents; //events tha occurred on fd }
要測試的條件由events成員指定,函數在相應的revents成語中返回該描述字的狀態。(每個描述字都有兩個變量,一個為調用值,另一個為返回結果,從而避免使用值-結果參數,這與select函數是不同的)。下圖列出了用于指定events標志以及測試revents標志的一些常值。
上圖需要注意的是,POLLERR,POLLHUP,POLLNVAL是處理錯誤的描述字,因此它們也就不可以出現在input事件中,即events。poll識別三類數據:普通(normal),優先級帶(priority band)和高優先級(high priority)。
對TCP和UPD而言,以下條件引起poll返回特定的revents。
1、 All regular TCP data and all UDP data is considered normal.
2、 TCP's out-of-band data (Chapter 24) is considered priority band.
3、 When the read half of a TCP connection is closed (e.g., a FIN is received), this is also considered normal data and a subsequent read operation will return 0.
4、 The presence of an error for a TCP connection can be considered either normal data or an error (POLLERR). In either case, a subsequent read will return –1 with errno set to the appropriate value. This handles conditions such as the receipt of an RST or a timeout.
5、 The availability of a new connection on a listening socket can be considered either normal data or priority data. Most implementations consider this normal data.
6、 The completion of a nonblocking connect is considered to make a socket writable.
——《unix網絡編程》第三版
參數nfds,指示結構數組中元素的個數。
參數timeout:
與select中的timeout不同,poll函數的timeout參數是一int值,表示poll函數返回前等待多長時間,它是毫秒級別的。它有三種情況的取值:1、INFTIM(一個負數值),表示永遠等待,即一直阻塞。2、0,表示立即返回,非阻塞。3、>0,表示正待指定數目的毫秒數。
poll函數的返回值:
當poll發生錯誤時,poll函數的返回值-1,若定時器時間到之前沒有任何描述字就緒,則返回0,否則返回就緒描述字的個數,即其revents成員值非0的描述字個數。
如果我們不再關心某個特定描述字,那么可以把與他對應的pollfd結構的fd成員設置成一個負值。poll函數將忽略這樣的pollfd結構的events成員,返回時將它的revents成員的值置為0。
poll函數的通信列子:一個簡單的TCP回射服務器程序
pollServer.c:使用select機制的服務器程序
#include <stdio.h> #include <string.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <poll.h> /*環境為ubuntu10.04自帶c環境,無法自動引入下列宏,所以自己寫在前面了*/ #define INFTIM -1 #define POLLRDNORM 0x040 /* Normal data may be read. */ #define POLLRDBAND 0x080 /* Priority data may be read. */ #define POLLWRNORM 0x100 /* Writing now will not block. */ #define POLLWRBAND 0x200 /* Priority data may be written. */ #define MAXLINE 1024 #define OPEN_MAX 16 //一些系統會定義這些宏 #define SERV_PORT 10001 int main() { int i , maxi ,listenfd , connfd , sockfd ; int nready; int n; char buf[MAXLINE]; socklen_t clilen; struct pollfd client[OPEN_MAX]; struct sockaddr_in cliaddr , servaddr; listenfd = socket(AF_INET , SOCK_STREAM , 0); memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERV_PORT); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); bind(listenfd , (struct sockaddr *) & servaddr, sizeof(servaddr)); listen(listenfd,10); client[0].fd = listenfd; client[0].events = POLLRDNORM; for(i=1;i<OPEN_MAX;i++) { client[i].fd = -1; } maxi = 0; for(;;) { nready = poll(client,maxi+1,INFTIM); if (client[0].revents & POLLRDNORM) { clilen = sizeof(cliaddr); connfd = accept(listenfd , (struct sockaddr *)&cliaddr, &clilen); for(i=1;i<OPEN_MAX;i++) { if(client[i].fd<0) { client[i].fd = connfd; client[i].events = POLLRDNORM; break; } } if(i==OPEN_MAX) { printf("too many clients! \n"); } if(i>maxi) maxi = i; nready--; if(nready<=0) continue; } for(i=1;i<=maxi;i++) { if(client[i].fd<0) continue; sockfd = client[i].fd; if(client[i].revents & (POLLRDNORM|POLLERR)) { n = read(client[i].fd,buf,MAXLINE); if(n<=0) { close(client[i].fd); client[i].fd = -1; } else { buf[n]='\0'; printf("Socket %d said : %s\n",sockfd,buf); write(sockfd,buf,n); //Write back to client } nready--; if(nready<=0) break; //no more readable descriptors } } } return 0; }
客戶端程序參考上一篇文章。
總結:
本文介紹了poll函數的原型,參數說明,注意事項以及一個簡單的代碼例子。在unix后續版本中,加入了epoll函數I/O復用機制,它在一定條件下更加高效,在以后的文章中,會對epoll機制再進行詳細的描述。之前在學習python的時候,也接觸了select和poll,但是當時了解的比較淺顯,希望通過最近的學習可以對unix下I/O復用有更深入的認識。
不含病毒。www.avast.com |
留言列表