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] = 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()

Last updated