(二十四)深入淺出TCPIP之 如何理解poll

2021-03-02 遊戲開發司機

  poll的機制與select類似,與select在本質上沒有多大差別,管理多個描述符也是進行輪詢,根據描述符的狀態進行處理,但是poll沒有最大文件描述符數量的限制。poll和select同樣存在一個缺點就是,包含大量文件描述符的數組被整體複製於用戶態和內核的地址空間之間,而不論這些文件描述符是否就緒,它的開銷隨著文件描述符數量的增加而線性增大。

2 poll函數解析

  函數格式如下所示:

# include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);

pollfd結構體定義如下:

struct pollfd {

int fd;         /* 文件描述符 */
short events;         /* 等待的事件 */
short revents;       /* 實際發生了的事件 */
} ; 

  每一個pollfd結構體指定了一個被監視的文件描述符,可以傳遞多個結構體,指示poll()監視多個文件描述符。每個結構體的events域是監視該文件描述符的事件掩碼,由用戶來設置這個域。revents域是文件描述符的操作結果事件掩碼,內核在調用返回時設置這個域。events域中請求的任何事件都可能在revents域中返回。合法的事件如下:

  POLLIN         有數據可讀。

  POLLRDNORM       有普通數據可讀。

  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       可用內存不足,無法完成請求。

3 demo演示

  編寫一個echo server程序,功能是客戶端向伺服器發送信息,伺服器接收輸出並原樣發送回給客戶端,客戶端接收到輸出到終端。

  伺服器端程序如下:

// FileName: server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <poll.h>
#include <unistd.h>
#include <sys/types.h>

#define IPADDRESS "127.0.0.1"
#define PORT 8787
#define MAXLINE 1024
#define LISTENQ 5
#define OPEN_MAX 1000
#define INFTIM -1

//函數聲明
//創建套接字並進行綁定
static int socket_bind(const char* ip,int port);
//IO多路復用poll
static void do_poll(int listenfd);
//處理多個連接
static void handle_connection(struct pollfd *connfds,int num);

int main(int argc,char *argv[])
{
int listenfd,connfd,sockfd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen;
listenfd = socket_bind(IPADDRESS,PORT);
listen(listenfd,LISTENQ);
do_poll(listenfd);
return 0;
}

static int socket_bind(const char* ip,int port)
{
int listenfd;
struct sockaddr_in servaddr;
listenfd = socket(AF_INET,SOCK_STREAM,0);
if (listenfd == -1)
{
perror("socket error:");
exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET,ip,&servaddr.sin_addr);
servaddr.sin_port = htons(port);
if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
{
perror("bind error: ");
exit(1);
}
return listenfd;
}

static void do_poll(int listenfd)
{
int connfd,sockfd;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen;
struct pollfd clientfds[OPEN_MAX];
int maxi;
int i;
int nready;
//添加監聽描述符
clientfds[0].fd = listenfd;
clientfds[0].events = POLLIN;
//初始化客戶連接描述符
for (i = 1;i < OPEN_MAX;i++)
clientfds[i].fd = -1;
maxi = 0;
//循環處理
for ( ; ; )
{
//獲取可用描述符的個數
nready = poll(clientfds,maxi+1,INFTIM);
if (nready == -1)
{
perror("poll error:");
exit(1);
}
//測試監聽描述符是否準備好
if (clientfds[0].revents & POLLIN)
{
cliaddrlen = sizeof(cliaddr);
//接受新的連接
if ((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1)
{
if (errno == EINTR)
continue;
else
{
perror("accept error:");
exit(1);
}
}
fprintf(stdout,"accept a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
//將新的連接描述符添加到數組中
for (i = 1;i < OPEN_MAX;i++)
{
if (clientfds[i].fd < 0)
{
clientfds[i].fd = connfd;
break;
}
}
if (i == OPEN_MAX)
{
fprintf(stderr,"too many clients.\n");
exit(1);
}
//將新的描述符添加到讀描述符集合中
clientfds[i].events = POLLIN;
//記錄客戶連接套接字的個數
maxi = (i > maxi ? i : maxi);
if (--nready <= 0)
continue;
}
//處理客戶連接
handle_connection(clientfds,maxi);
}
}

static void handle_connection(struct pollfd *connfds,int num)
{
int i,n;
char buf[MAXLINE];
memset(buf,0,MAXLINE);
for (i = 1;i <= num;i++)
{
if (connfds[i].fd < 0)
continue;
//測試客戶描述符是否準備好
if (connfds[i].revents & POLLIN)
{
//接收客戶端發送的信息
n = read(connfds[i].fd,buf,MAXLINE);
if (n == 0)
{
close(connfds[i].fd);
connfds[i].fd = -1;
continue;
}
// printf("read msg is: ");
write(STDOUT_FILENO,buf,n);
//向客戶端發送buf
write(connfds[i].fd,buf,n);
}
}
}

客戶端代碼如下所示:

 

// FileName: client.c
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <poll.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>

#define MAXLINE 1024
#define IPADDRESS "127.0.0.1"
#define SERV_PORT 8787

#define max(a,b) (a > b) ? a : b

static void handle_connection(int sockfd);

int main(int argc,char *argv[])
{
int sockfd;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET,IPADDRESS,&servaddr.sin_addr);
connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
//處理連接描述符
handle_connection(sockfd);
return 0;
}

static void handle_connection(int sockfd)
{
char sendline[MAXLINE],recvline[MAXLINE];
int maxfdp,stdineof;
struct pollfd pfds[2];
int n;
//添加連接描述符
pfds[0].fd = sockfd;
pfds[0].events = POLLIN;
//添加標準輸入描述符
pfds[1].fd = STDIN_FILENO;
pfds[1].events = POLLIN;
for (; ;)
{
poll(pfds,2,-1);
if (pfds[0].revents & POLLIN)
{
n = read(sockfd,recvline,MAXLINE);
if (n == 0)
{
fprintf(stderr,"client: server is closed.\n");
close(sockfd);
}
write(STDOUT_FILENO,recvline,n);
}
//測試標準輸入是否準備好
if (pfds[1].revents & POLLIN)
{
n = read(STDIN_FILENO,sendline,MAXLINE);
if (n == 0)
{
shutdown(sockfd,SHUT_WR);
continue;
}
write(sockfd,sendline,n);
}
}
}

4、程序測試結果

server端:


5、參考資料

http://blog.endlesscode.com/2010/03/27/select-poll-epoll-intro/

http://hi.baidu.com/xzf20082004/item/622fb01a1018c7f5746a846f

相關焦點

  • (二十五)深入淺出TCPIP之 epoll和select,poll的區別
    如果你對epoll,poll,select仍然一知半解,建議你可以閱讀  (十八)深入淺出TCPIP之epoll的一些思考
  • Exit poll?
    Reader question:Please explain 「exit poll」 in this sentence: At least one exit poll has HillaryLiterally, 「exit poll」 means a poll at the exit of the polls.Sorry about the confusion.
  • 如何教出熱愛閱讀的學生?需要老師「深入淺出」
    這就需要老師在教學中做到「深入淺出」,一步一步引導學生。「嚴謹治學」和「深入淺出」,是北京四中的教學箴言。歷屆四中校長常以「嚴謹治學」勉勵教師,強調教學要像學者治學那樣嚴謹。除此之外,他還是商務印書館《學生國學叢書新編》特約審稿人,曾受「中語會」之邀,講授國家級示範課《<論語>之孝》。其授課視頻《子路、曾晳、冉有、公西華侍坐》被收入人民教育出版社「部編本」《教師教學用書》。
  • 二十四節氣從哪來,今天我們該如何保護二十四節氣?
    今天,我們該如何保護二十四節氣「二十四節氣之始是冬至,而不是立春。這是因為第一節氣的核心,是太陽和月亮的『朔旦冬至』。就是說,在這個時刻,太陽和月亮的黃經正好相等。其他二十三個節氣,都不具備『朔旦』的條件,第一的位子毫無爭議地應當讓冬至來承擔。」
  • 深入淺出理解SerDes
    我還是習慣用思維框圖來講解一些比較特殊的總線,這樣可以從歷史(來源)、組成及對比等多方面理解一個複雜的東西,下面就針對SerDes這一串行總線深入淺出的進行分析,因為要深入淺出的分析,所以會拋棄一些細節,希望大家理解。
  • 深入淺出數據分析
    深入淺出系列,同系列的還有《深入淺出統計學》、《深入淺出Python》、《深入淺出SQL》等,當然這本書讀完以後強烈建議接著讀《深入淺出統計學》,加深對數據分析和統計學的理解和鞏固。作者:[美] Michael Milton豆瓣評分:7.5出版日期:2012年推薦指數:5顆星數據分析的流程 第一章講述了數據分析的流程,從提出問題到做出決策,中間的過程具體到每一步,和我們現在一直在強調的流程其實大差不差,數據是無處不在的,如何將原始數據轉變成推進工作的妙策
  • 深入淺出數據分析書單
    當然正如標題「深入淺出」,在考慮書單的時候儘量都是些淺顯易懂又不缺乏深度的書籍。統計學深入淺出統計學深入淺出系列書籍,使用大量圖片和對話,使得統計理論的學習既有趣又自然。對於入門統計學來說很重要一點就是淺顯易懂,學起來非常輕鬆,當然這本書還挺厚的,不過翻起來很快,打包票不會被勸退。
  • 今天,我們該如何保護二十四節氣
    那麼今天,我們是否還需要二十四節氣?而當我們談論保護二十四節氣時,我們保護的是什麼?又該如何保護?二十四節氣從哪裡來「春雨驚春清谷天,夏滿芒夏暑相連。秋處露秋寒霜降,冬雪雪冬小大寒。」這首被收錄於小學語文課本的《二十四節氣歌》,相信很多人耳熟能詳。
  • People unhappy with the rich: Poll
    There is growing dissatisfaction toward rich people, according to a new online poll.The poll by the China Youth Daily in collaboration with Sina.com has highlighted the apparent discontent over the country's widening income gap.
  • 深入淺出,得意忘言
    說不清楚,是你領悟不足,沒法宣之於口,這是理解的第一步,矢口而出謂之知,射,必要身正才能中的。身不行之,矢口出不來,或者不準的。所以說不出來和忘言是兩回事,這就是純碎的不知,或者摸到一點但沒理解透,說不出來呢。真正行動過,踐行過,總有辦法,當然未必都是直接的語言,來闡述清楚。各種比賦興都是可以用的^_^。所以深入淺出才是得意之循序漸進的正理。
  • 二十四節氣美圖咋拍出來的?這裡有竅門
    驚蟄·鋤禾 在圖片傳播成為「全民所有」的當下,在「一帶一路」的經濟和文化的世界合作、交流中,讓中國文化更加自信地面對世界,攝影人該如何詮釋中國文化的精髓內涵,如何拍攝好自己國家的二十四節氣文化圖標更有必要。
  • Obama, Romney deadlocked in U.S. presidential race: poll
    President Barack Obama and his Republican challenger Mitt Romney are still neck and neck in the 2012 presidential race despite their early campaign advertising blitz, according to a poll released on Tuesday
  • 「二十四節氣」如何活化與傳承
    同時,他認為,二十四節氣作為一個整體,其價值才能完整顯現。    現在如何融入人們日常生活    「春雨驚春清谷天,夏滿芒夏暑相連。秋處露秋寒霜降,冬雪雪冬小大寒……」這首古老的民俗歌謠也隨著「二十四節氣」的成功申遺再次走紅。然而,「二十四節氣」對現代人們日常生活有什麼樣的價值?知道「二十四節氣」的人又有多少?
  • 《頤和園二十四節氣日曆》|展示頤和園景色之四季更迭
    系列的首發它將實用性與文物的觀賞性集於一體北京頤和園管理處與文物出版社傾情奉獻讓更多人欣賞到中國園林建築的藝術之美《頤和園二十四節氣日曆·2020》| 文物出版社千百年來,園林作為中國建築的主要表現形式
  • select、pselect和poll函數的區別及用法
    下面我們說一下select、pselect和poll函數的具體用法及區別• select
  • 初學音樂者如何簡單理解「二十四個大小調」
    初學者第一步除了需要練習音階外,就是了解和熟悉「二十四個大小調」了。簡單來講,以鋼琴為例,上面每一組音階不是有白鍵加黑鍵十二個音嗎?白鍵音名分別是C D E F G A B,黑鍵則為升C、降E、升F、升G、降B。
  • linux開發各種I/O操作簡析,以及select、poll、epoll機制的對比
    3、poll方式: 中斷方式雖然佔用CPU資源少,但是在應用程式上需要不斷在死循環裡面執行讀取函數,應用程式不能去做其它事情。poll機制解決了這個問題,當有事件發生時,才去執行讀read函數,按鍵事件沒有按下時<如果規定了時間,超過時間後返回無按鍵信息>,去執行其它的處理函數。
  • 深入淺出SQL教程之Group by和Having
    首頁 > 語言 > 關鍵詞 > 最新資訊 > 正文 深入淺出SQL教程之Group by和Having
  • 二十四節氣中的詩畫江南
    《大地的耳語   ——江南二十四節氣》在這樣的日子裡,坐在陽臺上,打開王寒新出版的《大地的耳語——江南二十四節氣》,一口氣讀下去,一天就經歷了二十四節氣、一年四季的輪迴。   王寒的作品,一直根植於江南大地。在她眼裡,江南的一花一果、一山一水,都值得書寫,所以有了《浙江有意思》《無鮮勿落飯》《江南草木記》和《大地的耳語——江南二十四節氣》「江南風雅頌」系列。
  • 如何給老外介紹二十四節氣呢?
    新東方網>英語>英語學習>語法詞彙>流行語>正文如何給老外介紹二十四節氣呢?二十四節氣算是我們中國的一大特色。古時用來幫助耕種,現在可以提醒人們注意健康。這麼一個本土的玩意兒,老外要會說嗎?二十四節氣的英語是哪些呢?