9.17. poll()

同時對多個 socket 進行事件測試

函式原型
#include <sys/poll.h>

int poll(struct pollfd *ufds, unsigned int nfds, int timeout);

說明

這個函式非常類似 select(),它們兩者都監看整組 file descriptor 的事件,比如進入的資料是就緒可收[ready to recv()]、socket 是就緒可送[ready to send()] 資料、out-of-band 資料是就緒可收[ready to recv()]、錯誤等。

基本的想法是,你透過 ufds 傳遞一個有 nfds 個 struct pollfd 的陣列,並以毫秒(millisecond)為單位設定 timeout 時間(一秒有 1,000 毫秒),若陣列中的每個 socket descriptor 都沒有事件發生時,則隨著 timeout 時間耗盡,poll() 就會返回。如果你不需要 timeout,要讓 poll() 一直等待,那麼可以將 timeout 設定為負值。

陣列中的每個 struct pollfd 元素(element)表示一個 socket descriptor,且包含了下列的欄位:
struct pollfd {
    int fd;         // the socket descriptor
    short events;   // bitmap of events we're interested in
    short revents;  // when poll() returns, bitmap of events that occurred
};
在呼叫 poll() 以前,將 fd 帶入 socket descriptor 的值(若你將 fd  設定為負值,這個 struct pollfd 就會被忽略掉,並且將它的 revents 欄位設定為零),接著用 OR 位元運算下列的 macros(巨集)以建構 events 欄位:
POLLIN          當這個 socket 上的資料已經就緒可以收[recv()]時,通知(alert)我。
POLLOUT         當我可以送[send()]資料到這個 socket 而不會 blocking 時,通知我。
POLLPRI         當 out-of-band data 就緒可收時,通知我。
一旦 poll() call 返回時,會透過對上述欄位進行 OR 位元運算來建構 revents 欄位,以告訴你哪些 descriptor 目前已經有事件發生,此外,其它的欄位可能會保持原值:
POLLERR         在這個 socket 上已經發生錯誤
POLLHUP         遠端連線已經斷線
POLLNVAL        fd socket descriptor 有點問題,或許是因為沒有初始化?

傳回值
傳回 ufds 陣列中,有事件發生的元素(element)數量;如果發生 timeout 時,這個值會是零。錯誤時會傳回 -1(並設定相對應的 errno)。

範例
int s1, s2;
int rv;
char buf1[256], buf2[256];
struct pollfd ufds[2];

s1 = socket(PF_INET, SOCK_STREAM, 0);
s2 = socket(PF_INET, SOCK_STREAM, 0);

// 假設此時我們已經都連線到 server 了
//connect(s1, ...)...
//connect(s2, ...)...

// 設定 file descriptors 陣列
//
// 在此例中,我們想要知道何時有就緒的一般資料或 out-of-band(頻外)資料要收
// 

ufds[0].fd = s1;
ufds[0].events = POLLIN | POLLPRI; // 要檢查是一般資料或 out-of-band 資料

ufds[1].fd = s2;
ufds[1].events = POLLIN; // 只檢查一般的資料 // 等待 sockets 上的事件,timeout 時間是 3.5 秒 rv = poll(ufds, 2, 3500); if (rv == -1) { perror("poll"); // poll() 時發生錯誤 } else if (rv == 0) { printf("Timeout occurred! No data after 3.5 seconds.\n"); } else { // 檢查 s1 上的事件: if (ufds[0].revents & POLLIN) { recv(s1, buf1, sizeof buf1, 0); // 接收一般的資料 } if (ufds[0].revents & POLLPRI) { recv(s1, buf1, sizeof buf1, MSG_OOB); // out-of-band 資料 } // 檢查 s2 上的事件: if (ufds[1].revents & POLLIN) { recv(s2, buf2, sizeof buf2, 0); } }
參考
select()
Comments