📘
Beej's Guide to Network Programming 正體中文版
  • 簡介
  • 原著資訊
  • 譯者誌謝
  • 進階資料
  • 簡體中文版
  • 中文授權
  • 聯絡譯者
  • 1. 導讀
    • 1.1. 本書的讀者
    • 1.2. 平台與編譯器
    • 1.3. 官方網頁與書本
    • 1.4. Solaris/SunOS 程式設計師該注意的事
    • 1.5. Windows 程式設計師該注意的事
    • 1.6. 來信原則
    • 1.7. 鏡射站台(Mirroring)
    • 1.8. 譯者該注意的
    • 1.9. 版權與散佈
  • 2. 何謂 Socket
    • 2.1 兩種 Internet Sockets
    • 2.2 底層漫談與網路理論
  • 3. IP address、結構與資料轉換
    • 3.1. IPv4 與 IPv6
      • 3.1.1. Sub network (子網段)
      • 3.1.2. Port Number(連接埠號碼)
    • 3.2. Byte Order(位元組順序)
    • 3.3. 資料結構
    • 3.4. IP 位址,續集
      • 3.4.1 Private Network
  • 4. 從 IPv4 移植為 IPv6
  • 5. System call 或 Bust
    • 5.1. getaddrinfo()-準備開始!
    • 5.2. socket()-取得 File Descriptor!
    • 5.3. bind()- 我在哪個 port?
    • 5.4. connect(),嘿!你好。
    • 5.5. listen()-有人會呼叫我嗎?
    • 5.6. accept()- 謝謝你 call 3490 port
    • 5.7. send() 與 recv()- 寶貝,我們來聊天!
    • 5.8. sendto() 與 recvfrom()- 來點 DGRAM
    • 5.9. close() 與 shutdown()- 你消失吧!
    • 5.10. getpeername()-你是誰?
    • 5.11. gethostname()-我是誰?
  • 6. Client-Server 基礎
    • 6.1. 簡單的 Stream Server
    • 6.2. 簡單的 Stream Client
    • 6.3. Datagram Sockets
  • 7. 進階技術
    • 7.1. Blocking(阻塞)
    • 7.2. select():同步 I/O 多工
    • 7.3. 不完整傳送的後續處理
    • 7.4. Serialization:如何封裝資料
    • 7.5. 資料封裝
    • 7.6. 廣播封包:Hello World!
  • 8. 常見的問題
  • 9. Man 使用手冊
    • 9.1. accept()
    • 9.2. bind()
    • 9.3. connect()
    • 9.4. close()
    • 9.5. getaddrinfo(), freeaddrinfo(), gai_strerror()
    • 9.6. gethostname()
    • 9.7. gethostbyname(), gethostbyaddr()
    • 9.8. getnameinfo()
    • 9.9. getpeername()
    • 9.10. errno
    • 9.11. fcntl()
    • 9.12. htons(), htonl(), ntohs(), ntohl()
    • 9.13. inet_ntoa(), inet_aton(), inet_addr
    • 9.14. inet_ntop(), inet_pton()
    • 9.15. listen()
    • 9.16. perror(), strerror()
    • 9.17. poll()
    • 9.18. recv(), recvfrom()
    • 9.19. select()
    • 9.20. setsockopt(), getsockopt()
    • 9.21. send(), sendto()
    • 9.22. shutdown()
    • 9.23. socket()
    • 9.24. struct sockaddr and pals
  • 10. 參考資料
    • 10.1. 書籍
    • 10.2. 網站參考資料
    • 10.3. RFC
  • 11. 原著誌謝
Powered by GitBook
On this page
  • 函式原型
  • 說明
  • 傳回值
  • 範例
  • 參考
Edit on GitHub
  1. 9. Man 使用手冊

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

Previous9.16. perror(), strerror()Next9.18. recv(), recvfrom()

Last updated 2 years ago