📘
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.19. select()

檢查 sockets descriptors 是否就緒可讀或可寫

函式原型

#include <sys/select.h>

int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
           struct timeval *timeout);

FD_SET(int fd, fd_set *set);
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_ZERO(fd_set *set);

說明

select() 函式讓你可以同步檢查多個 sockets,檢查它們是否有資料需要接收,或者是否你可以送出資料而不會發生 blocking,或者是否有例外發生。

你可以使用如上述的 FD_SET() macros(巨集)來調整 socket descriptors set。

如果你想要知道 set 上的哪些 sockets 有資料可以接收(就緒可讀),則將這個 set 放在 readfds 參數;

如果你想要知道 set 上的哪些 sockets 有資料可以傳送(就緒可寫),則將這個 set 放在 writefds 參數;

若你想知道哪些 sockets 有例外(錯誤)發生,則可以將 set 擺在 exceptfds 參數。

沒興趣知道的事件可以將 select() 相對應的參數設定為 NULL。

在 select() 返回之後,這些 sets 的值會改變,用以標示哪些 sockets 是就緒可讀、可寫,及有例外發生等事件。

第一個參數 n 的值是最大的那個 socket descriptor 數值在加上 1。

最後,struct timeval 可以設定 timeout 的時間,用來告訴 select() 要等待多久的時間。 select() 會在有任何事件發生或 timeout 之後返回。struct timeval 有兩個欄位:tv_sec 設定秒(second)、tv_usec 設定微秒(microsecond)[一秒鐘有 1,000,000 微秒]。

用到的 macros 功能如下:

  • FD_SET(int fd, fd_set *set); 將 fd 新增至 set。

  • FD_CLR(int fd, fd_set *set); 從 set 中移除 fd。

  • FD_ISSET(int fd, fd_set *set); 若 fd 在 set 中則傳回 true。

  • FD_ZERO(fd_set *set); 將 set 清為零。

傳回值

成功時傳回 set 中的 descriptors 數量,若發生 timeout 時傳回 0,錯誤時傳回 -1(並設定相對應的 errno),還有,sets 會被改過,用以表示哪幾個 sockets 是已經就緒的。

範例

int s1, s2, n;
fd_set readfds;
struct timeval tv;
char buf1[256], buf2[256];

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

// 事先清除 set
FD_ZERO(&readfds);

// 將我們的 descriptors 新增到 set
FD_SET(s1, &readfds);
FD_SET(s2, &readfds);

// 因為 s2 是後來才取得的,所以它的數值會"比較大",所以我們用它作為 select() 中的 n 參數

n = s2 + 1;

// 在 timeout 以前會一直等待,看是否已經有資料可以接收的 socket(timeout 時間是 10.5 秒)
tv.tv_sec = 10;
tv.tv_usec = 500000;
rv = select(n, &readfds, NULL, NULL, &tv);

if (rv == -1) {
    perror("select"); // select() 發生錯誤
} else if (rv == 0) {
    printf("Timeout occurred!  No data after 10.5 seconds.\n");
} else {
    // 至少一個 descriptor(s) 有資料
    if (FD_ISSET(s1, &readfds)) {
        recv(s1, buf1, sizeof buf1, 0);
    }
    if (FD_ISSET(s2, &readfds)) {
        recv(s2, buf2, sizeof buf2, 0);
    }
}

參考

poll()

Previous9.18. recv(), recvfrom()Next9.20. setsockopt(), getsockopt()

Last updated 2 years ago