select()

select() 呼び出しは、一連のソケット上の活動をモニターして、読み取りまたは書き込みが可能なソケットや、例外条件が保留になっているソケットを探します。

#include <manifest.h>
#include <socket.h>
#include <bsdtypes.h>
#include <bsdtime.h>
 
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout)
パラメーター
説明
nfds
検査するソケット記述子の数。 検査するソケットのソケット番号の中で 最大のものより 1 だけ大きい値を指定する必要があります。

select() 呼び出しを使用して、検査したいソケット用のソケット記述子が入っているビット・セットを渡すことが できます。ビット・セットのサイズは固定です。使用可能になり得るすべてのソケットの それぞれについて 1 ビットずつが使用されます。 nfds パラメーターは、割り振られたソケット・ビット・セットの サブセットだけを検査することを select() に強制するために使用します。

アプリケーションがソケット 3、4、5、6、および 7 を割り振る場合に、すべての割り振りについて検査を行いたいときは、nfds には 8、つまり、指定したソケット記述子の中で最高のものに 1 を加算した値を設定する必要が あります。アプリケーションがソケット 3 と 4 を検査する場合は、nfds には 5 を設定する必要があります。

ソケット番号 0、1、および 2 は C ソケット・インターフェースによって 使用されるため、ソケット番号は 3 から順番に割り当てられ ます。

readfds
読み取りに使用可能かどうか検査される記述子のビット・セットを示します。
writefds
書き込みに使用可能かどうか検査される記述子のビット・セットを示します。
exceptfds
例外条件が保留状態になっているかどうか検査される記述子の ビット・セットを示します。
timeout
select() が完了するのを待つ時間を示します。

timeout が NULL ポインターではない場合 は、timeout は select() が完了する のを待つ時間の最大値を指定します。timeout が NULL ポインター である場合は、select() 呼び出しはいずれかのソケットが使用可能になるまでブロック を行います。ソケットのポーリング後即時に制御を戻すようにする ためには、timeout が、 値 0 を設定された timeval 構造体を指す非 NULL ポインターである必要があります。

ソケット記述子セットの中で AF_INET ソケットと AF_IUCV ソケットの両方を使用している場合は、 タイマーの値は無視され、 あたかも timeout が、 値 0 を設定された timeval 構造体を指す非 NULL ポインターであるかのように処理されます。

ユーザーのプログラムの中で select() をタイマーとして使用するには、以下のいずれかを行ってください。
  • 読み取り、書き込み、および例外配列を 0 に設定する。
  • nfds を NULL ポインターになるように設定する。

select() 呼び出しの使用方法を理解するためには、ソケットとポートとの違いを 理解する必要があります。TCP/IP では、ポートは特定のマシン上の特定のプロセスを 表すものとして定義されています。ポートはある 1 つのプロセスの位置を 表すのであって、プロセス間の接続を表すわけではありま せん。TCP/IP 用の MVS™ プログラミング・インターフェース においては、ソケットは通信のエンドポイントを記述します。したがって、ソケットはポートとマシンの両方を 記述します。ファイル記述子と同様に、ソケットは 1 つの TCP/IP アドレス・スペース内の 通信エンドポイントのテーブルに対する索引を表す短整数です。

一度に複数のソケットをテストするには、テストするソケットを FD_SET タイプの ビット・セットに入れてください。 ビット・セットはビットのストリングで、X がこのセットのエレメントであるとき、X を表すビットは 1 に設定されます。 X がこのセットのエレメントでないとき、X を表すビットは 0 に設定されます。 例えば、ソケット 33 があるビット・セットのエレメントである場合、ビット 33 は 1 に設定されます。 ソケット 33 がビット・セットのエレメントでない場合、ビット 33 は 0 に設定されます。

ビット・セットには 1 つのプロセスが割り振ることができるソケットのすべてについて それぞれ 1 つずつ用意されるビットが入っているため、ビット・セットのサイズは定数 です。関数 getdtablesize() は、ユーザー・プログラムが割り振ることができる ソケットの数を戻します。使用するプログラムが多数のソケットを割り振ることを必要と する場合は、getdtablesize() および maxdesc() を使用して、割り振り可能なソケット の数を増やしてください。ビット・セットのサイズの増加はプログラムのコンパイル時に 行う必要があります。ビット・セットのサイズを大きくするには、BSDTYPES.H を 組み込む前に FD_SETSIZE を定義してください。 FD_SETSIZE はあらゆるソケットの値の中で最大の値です。255 in BSDTYPES.H では 255 と定義されています。

ビット・セットを操作することができるマクロを以下に示します。
マクロ
説明
FD_ZERO(&fdset)
ビット・セット fdset の中のすべてのビットを 0 に設定します。 この操作の実行後は、そのビット・セットは、ソケットをエレメントとして含まなくなります。 FD_SET() を呼び出してソケットをメンバーとして設定する前に、このマクロを 呼び出して当該のビット・セットを初期設定する必要があります。
FD_SET(sock, &fdset)
ソケット sock 用のビットを 1 に設定します。これに より、sock がビット・セット fdset のメンバーになります。
FD_CLR(sock, &fdset)
ビット・セット fdset の中の ソケット sock 用のビットをクリアします。 この操作によって該当するビットが 0 に設定されます。
FD_ISSET(sock, &fdset)
sock がビット・セット fdsetのメンバーである場合は、非ゼロを戻します。sockfdset の メンバーでない場合は 0 を戻します。 (この操作は sock を表すビットを戻します。)
ソケットが読み取り可能な状態になるのは、そのソケット用の着信データがバッファーに 入っているとき、および接続要求が保留状態になっているときです。accept()、read()、recv()、また は recvfrom() への呼び出しはブロックされません。読み取り可能な状態になっている ソケットがあるかどうかをテストするには、FD_ZERO() を使用して readfds ビット・セットを 初期設定し、テストするソケットのそれぞれに対して FD_SET() を起動してください。

ソケットが書き込み可能な状態になるのは、バッファーに発信データのためのスペースが あるときです。ソケットが読み取り可能な状態になるのは、 受信されるソケットにデータがあるときです。非ブロック・ストリーム・ソケットが接続プロセス中であった 場合は、connect() は -1 で戻ります。 プログラムは errno を検査する必要があります。 errno が EINPROGRESS であった場合、connect() が完了すると、 ソケットが書き込み用に選択されます。errno が EINPROGRESS でない場合でも、ソケットは書き込み用に選択され、 ソケットに保留エラーがあることが示されます。当該のデータの容量がバッファー・スペースの容量よりも 小さい場合は、write()、send()、または sendto() への呼び出しはブロックされ ません。ソケットが書き込みのために選択されたときは、最低限で も、getsockopt() と一緒に SO_SNDBUF を使用することによって戻されたサイズに相当 する量の使用可能バッファー・スペースが保証されます。書き込み可能な状態になって いるソケットがあるかどうかをテストするには、FD_ZERO() を使用して writefds を 初期設定し、テストするソケットのそれぞれに対して FD_SET() を使用してください。

select() 呼び出しは、ターゲット・プログラムが正常に takesocket() を呼び出した ことを指示することができるように、所定のソケット上で保留状態になっている例外条件 があるかどうかを検査します。 保留状態の例外条件があることが select() によって 示された場合は、ユーザー・プログラムは close() を呼び出してその所定のソケットを クローズします。ソケット上で例外条件が保留状態になるのは、そのソケットが アウト・オブ・バンド・データを受信したときです。別のアプリケーション が takesocket() を使用してそのソケットを正常に取得した場合は、givesocket() を 使用して提供されたストリーム・ソケットが例外用に選択されます。

プログラマーは、ビット・セットにテストすべきソケットがない場合は 任意に NULL を渡すことができます。 例えば、ソケットが読み取り可能かどうかの検査だけを必要とするプログラムの場合 は、writefdsexceptfds の 両方に NULL を渡すことができます。

一連のソケットはビット・セットの形で select() に渡されるため、select() 呼び出し はソケットの状況を調べるためのポーリングを行う前に各ビット・セット内の各ビットを テストする必要があります。効率を高めるために、各ビット・セットに入れられて渡され るすべてのソケットの中で最大のソケットを nfsd パラメーターで指定します。 このようにすると、select() 呼び出しは、番号が 0 か ら nfsd -1 までの範囲のソケットだけをテストします。 変数 nfsd は、getdtablesize() の実行結果であっても構い ません。ただし、ソケットを 2 つしかもっていないアプリケーションの場合 は、nfsd が getdtablesize() の実行結果であると、select() は各ビット・セットのすべてのビットをテストします。

戻り値

すべてのビット・セット内の使用可能なソケットの合計数が戻されます。 値 -1 はエラーを示します。errno を検査してください。値 0 は、時間制限によって 有効期限が切れたことを示します。戻り値が 0 より大きい場合は、 各ビット・セット内の作動可能なソケットは 1 に設定されます。 各ビット・セット内の作動可能でないソケットは 0 に設定されます。 各ソケットでマクロ FD_ISSET() を使用して、ソケットの状況をテストします。
Errno
説明
EBADF
ビット・セットのいずれか 1 つが誤ったソケットを指定しています。 [ソケットを設定する前に FD_ZERO() が呼び出されなかった可能性があります。]
EFAULT
ビット・セットのいずれか 1 つが、呼び出し側のアドレス・スペースの外側の値をポイントしています。
EINVAL
timeval 構造体のフィールドのいずれか 1 つが無効です。
注: 作動可能なソケットの数が 65535 よりも大きい場合は、65535 のみが報告されます。

以下の例で select() は、読み取り (socket r)、書き込み (socket w)、および 例外 (socket e) の各条件についてソケットをポーリングするために使用されています。
/* sock_stats(r, w, e) - Print the status of sockets r, w, and e. */
int sock_stats(r, w, e)
int r, w, e;
{
   fd_set reading, writing, except;
   struct timeval timeout;
   int rc, max_sock;
 
   /* initialize the bit sets */
   FD_ZERO( &reading );
   FD_ZERO( &writing );
   FD_ZERO( &except );
 
   /* add r, w, and e to the appropriate bit set */
   FD_SET( r, &reading );
   FD_SET( w, &writing );
   FD_SET( e, &except );
 
   /* for efficiency, what's the maximum socket number? */
   max_sock = MAX( r, w );
   max_sock = MAX( max_sock, e );
   max_sock ++;
 
   /* make select poll by sending a 0 timeval */
   memset( &timeout, 0, sizeof(timeout) );
 
   /* poll */
   rc = select( max_sock, &reading, &writing, &except, &timeout );
 
   if ( rc < 0 ) {
        /* an error occurred during the select() */
        tcperror( "select" );
   }
   else if ( rc == 0 ) {
        /* none of the sockets were ready in our little poll */
        printf( "nobody is home.¥n" );
   } else {
        /* at least one of the sockets is ready */
        printf("r is %s¥n", FD_ISSET(r,&reading) ? "READY" : "NOT READY");
        printf("w is %s¥n", FD_ISSET(w,&writing) ? "READY" : "NOT READY");
        printf("e is %s¥n", FD_ISSET(e,&except)  ? "READY" : "NOT READY");
   }
}
 

関連する呼び出し

getdtablesize()、maxdesc()、selectex()