9.19 select()

ソケット記述子が読み込み/書き込み可能かどうか確認します。

9.19.1 書式

#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);

9.19.2 解説

select() 関数は、複数のソケットを同時にチェックして、受信待ちのデータがあるか、ブロッキングせずに send() できるか、何らかの例外が発生したかを確認する方法を提供します。

上記の FD_SET() のようなマクロを使用して、ソケット記述子のセットを投入します。セットができたら、それを以下のいずれかのパラメータとして関数に渡します。readfds(セット内のいずれかのソケットが recv() データを受信できる状態になったとき)、writefds(セット内のいずれかのソケットが send() データを送信できる状態になったとき)、または exceptfds(いずれかのソケットで例外が発生したときに知る必要がある場合)。もし、これらのタイプのイベントに興味がなければ、これらのパラメータのいずれか、あるいはすべてを NULL にすることができます。select() が返された後、セット内の値が変更され、どれが読み書き可能で、どれが例外を発生させたかが表示されます。

最初のパラメータ n は、最も大きい番号のソケット記述子(これらは単なる ints, 覚えていますか?)に 1 を加えたものです。

最後に、最後に struct timeval, timeout を記述します。これは select() に、これらのセットをどれくらいの時間チェックするかを指示します。タイムアウト後、あるいはイベントが発生したときのどちらか早いほうに戻ります。timeval 構造体は2つのフィールドを持っています。tv_sec は秒数で、これに tv_usec というマイクロ秒(1,000,000 マイクロ秒が 1 秒)を加えたものです。

ヘルパーマクロは次のような働きをします。

Macro解説
FD_SET(int fd, fd_set *set);setfd を追加します。
FD_CLR(int fd, fd_set *set);set から fd を削除します。
FD_ISSET(int fd, fd_set *set);fdset に含まれる場合は true を返す。
FD_ZERO(fd_set *set);set からすべてのエントリをクリアします。

Linux ユーザーへの注意事項:Linux の select() は "ready-to-read" を返すことがありますが、実際には読み込む準備ができていないため、その後の read() 呼び出しがブロックされることがあります。このバグを回避するには、受信側のソケットに O_NONBLOCK フラグを設定し、EWOULDBLOCK でエラーになるようにし、このエラーが発生しても無視します。ソケットをノンブロッキングに設定する方法については、fcntl() の man ページ を参照してください。

9.19.3 返り値

成功した場合はセットに含まれる記述子の数を、タイムアウトに達した場合は 0 を、エラーの場合は -1 を返します(それに応じて errno が設定されます)。また、どのソケットが準備できているかを示すために、セットも変更されます。

9.19.4 例

int s1, s2, n; fd_set readfds; struct timeval tv; char buf1[256], buf2[256]; // pretend we've connected both to a server at this point //s1 = socket(...); //s2 = socket(...); //connect(s1, ...)... //connect(s2, ...)... // clear the set ahead of time FD_ZERO(&readfds); // add our descriptors to the set FD_SET(s1, &readfds); FD_SET(s2, &readfds); // since we got s2 second, it's the "greater", so we use that for // the n param in select() n = s2 + 1; // wait until either socket has data ready to be recv()d (timeout 10.5 secs) tv.tv_sec = 10; tv.tv_usec = 500000; rv = select(n, &readfds, NULL, NULL, &tv); if (rv == -1) { perror("select"); // error occurred in select() } else if (rv == 0) { printf("Timeout occurred! No data after 10.5 seconds.\n"); } else { // one or both of the descriptors have data if (FD_ISSET(s1, &readfds)) { recv(s1, buf1, sizeof buf1, 0); } if (FD_ISSET(s2, &readfds)) { recv(s2, buf2, sizeof buf2, 0); } }

9.19.5 参照

poll()