WSAAsyncSelect

WSAAsyncSelect関数は、ソケットに対してWindowsメッセージによるネットワークイベント通知を要求します。

int WSAAsyncSelect(
    SOCKET s,           // socket discriptor
    HWND hWnd,          // window handle
    unsigned int wMsg,  // message code
    long lEvent         // bitmask
);

WS2_32.DLL

引数

s

イベント通知を要求するソケットを識別するディスクリプタを指定します。

hWnd

ネットワークイベントが発生した際にメッセージを受信するウィンドウを識別するハンドルを指定します。

wMsg

ネットワークイベントが発生した際に受信するメッセージを指定します。

lEvent

アプリケーションが関心のあるネットワークイベントの組み合わせを示すビットマスクを指定します。以下の表に示す値のビット論理和で指定します。

0x0001 (FD_READ)

読み取り可能となったことの通知を受け取るように設定します。

0x0002 (FD_WRITE)

書き込み可能となったことの通知を受け取るように設定します。

0x0004 (FD_OOB)

OOBデータ到着の通知を受け取るように設定します。

0x0008 (FD_ACCEPT)

外部からの接続の通知を受け取るように設定します。

0x0010 (FD_CONNECT)

コネクション確立またはマルチポイントjoin操作の通知を受け取るように設定します。

0x0020 (FD_CLOSE)

ソケットが閉じられたことの通知を受け取るように設定します。

0x0040 (FD_QOS)

ソケットのサービス品質(QOS)変更の通知を受け取るように設定します。

0x0080 (FD_GROUP_QOS)

ソケットグループのサービス品質(QOS)の変更の通知を受け取るように設定します。

0x0100 (FD_ROUTING_INTERFACE_CHANGE)

指定された接続先へのルーティングインターフェース変更の通知を受け取るように設定します。

0x0200 (FD_ADDRESS_LIST_CHANGE)

ソケットプロトコルファミリに対するローカルアドレスリスト変更の通知を受け取るように設定します。

このパラメータに0を指定すると、すべての通知をキャンセルしてWindows Socketsがソケットに関するネットワークイベントのメッセージをこれ以上送信しないようにします。

戻り値

成功すると、アプリケーションのネットワークイベントセットへの関心の宣言が成功したならば、戻り値は0になります。

失敗すると-1 (SOCKET_ERROR) を返します。特定のエラーコードを取得するにはWSAGetLastError関数を呼び出します。

10022 (WSAEINVAL)

ウィンドウハンドルが既存のウィンドウを参照していなかったり、指定されたソケットが有効な状態ではない等、いずれかのパラメータが有効ではありません。

10036 (WSAEINPROGRESS)

ブロッキングWindows Sockets 1.1呼び出しが実行中であるか、サービスプロバイダがコールバック関数を実行中です。

10038 (WSAENOTSOCK)

指定されたディスクリプタはソケットではありません。

10050 (WSAENETDOWN)

ネットワークサブシステムが失敗しました。

10093 (WSANOTINITIALISED)

この関数を呼び出す前にWSAStartup関数の呼び出しが成功していなければなりません。

アプリケーションウィンドウがメッセージを受け取る際に、追加のエラーコードがセットされる可能性があります。このエラーコードは応答メッセージのlParamパラメータからWSAGETSELECTERRORマクロ(上位ワードを取り出す)で取り出すことができます。それぞれのネットワークイベントに対して起こりうるエラーコードを以下の表に示します。

解説

WSAAsyncSelect関数は、lEventパラメータで指定されたいずれかのネットワークイベントを検出した時に、WS2_32.DLLがウィンドウhWndにメッセージを送信するように要求するために使用します。送信されるメッセージをwMsgパラメータで、通知が必要なソケットをsパラメータで指定します。

WSAAsyncSelect関数は、lEventの値に関わらずソケットsを自動的に非ブロッキングモード設定します。ソケットsをブロッキングモードに戻すには、まずlEvent0を指定してWSAAsyncSelect関数を呼び出し、ソケットsに関連付けられているイベントレコードをクリアする必要があります。その後、ioctlsocketまたはWSAIoctl関数を呼び出してソケットをブロッキングモードに戻すことができます。

WSAAsyncSelect関数を発行すると、それまでの同じソケットに対するWSAAsyncSelectまたはWSAEventSelect呼び出しはキャンセルされます。例えば、呼び出しおよび書き込みの両方に対する通知を受け取るには、アプリケーションはFD_READFD_WRITEの両方を指定してWSAAsyncSelectを呼び出さなければいけません。

すべての通知をキャンセルしてWindows Socketsがソケットに関するネットワークイベントのメッセージをこれ以上送信しないようにするには、lEvent0を指定します。

この場合、WSAAsyncSelect関数は直ちにソケットに関するイベントメッセージのポストを停止しますが、アプリケーションのメッセージキューでメッセージが待機中である可能性があります。そのため、アプリケーションはキャンセル操作以降であってもネットワークイベントメッセージを受け取ることができるようになっていなければいけません。closesocketでソケットをクローズした場合にもWSAAsyncSelectによるメッセージ送信がキャンセルされますが、キューにあるメッセージについて同様に注意を払う必要があります。

accept関数によって作成されたソケットは、そのソケットを受け取るのに使用されたリスニングソケットと同じ特性を持ちます。そのため、リスニングソケットに設定されているWSAAsyncSelectイベントがacceptが返すソケットにも適用されます。例えば、リスニングソケットがWSAAsyncSelectイベントFD_ACCEPT, FD_READ, FD_WRITEを持っており、そのリスニングソケット上で接続を受け付けたソケットもまたFD_ACCEPT, FD_READ, FD_WRITEイベントを持ち、同じwMsg値がメッセージ通知に使用されます。もし異なるwMsg値やイベントが必要である場合は、アプリケーションはWSAAsyncSelectを呼び出し、接続を受け付けたソケットと必要な新しいデータを渡す必要があります。

指定されたイベントのいずれかが指定されたソケットs上で発生すると、アプリケーションウィンドウhWndはメッセージwMsgを受信します。wParamパラメータはネットワークイベントが発生したソケットを表します。lParamパラメータの下位ワードは発生したネットワークイベントを表します。lParamパラメータの上位ワードにはエラーコードが格納されます。

Note: イベント通知メッセージの受信時にWSAGetLastError関数を使用してエラー値をチェックすることはできません。この関数が返す戻り値はlParamパラメータの上位ワードが示すエラー値と異なる可能性があります。

発生する可能性のあるネットワークイベントコードを以下の表に示します。

0x0001 (FD_READ)

ソケットsが読み取り可能です。

0x0002 (FD_WRITE)

ソケットsが書き込み可能です。

0x0004 (FD_OOB)

ソケットs上のOOBデータが読み取り可能です。

0x0008 (FD_ACCEPT)

ソケットsが外部からの新しい接続を受け取ることが可能です。

0x0010 (FD_CONNECT)

ソケットsで開始されたコネクションが確立またはマルチポイントjoin操作が完了しました。

0x0020 (FD_CLOSE)

ソケットsで識別されるコネクションが閉じられました。

0x0040 (FD_QOS)

ソケットsに関連付けられているサービス品質(QOS)が変更されました。

0x0080 (FD_GROUP_QOS)

この値は予約されています。ソケットsが属するソケットグループのサービス品質(QOS)が変更されました。(将来のソケットグループでの使用のために予約済み)

0x0100 (FD_ROUTING_INTERFACE_CHANGE)

指定された接続先へ送信するために使用されるローカルインターフェースが変更されました。

0x0200 (FD_ADDRESS_LIST_CHANGE)

アプリケーションクライアントがバインド可能なソケットプロトコルファミリのローカルアドレスのリストが変更されました。

複数のイベントの監視のためにWSAAsyncSelect関数を呼び出すことができますが、アプリケーションウィンドウはそれぞれのネットワークイベントごとに1つのメッセージを受け取ります。

select関数の場合と同様に、WSAAsyncSelect関数は、データ転送操作(sendrecv)が直ちに成功することを期待して呼び出せるタイミングを決定するために使用されます。ただし、堅牢なアプリケーションでは、メッセージを受け取ってWindows Sockets 2呼び出しを発行した場合でもWSAEWOULDBLOCKエラーが返される可能性があることを想定していなければいけません。例えば以下のイベントシーケンスが起こりえます。

  1. ソケットsでデータを受信し、Windows Sockets 2がWSAAsyncSelectメッセージをポストする
  2. アプリケーションが別のメッセージを処理する。
  3. メッセージ処理中に、アプリケーションがioctlsocket(s, FIONREAD,...) を呼び出し、読み取り可能なデータが存在すると判断する。
  4. アプリケーションがrecv(s,...) 呼び出しを発行してデータを読み取る。
  5. アプリケーションが次のメッセージを処理するためにループし、読み取り可能データがあることを示すWSAAsyncSelectメッセージに到達する。
  6. アプリケーションがrecv(s,...) 関数を呼び出すと、WSAEWOULDBLOCKエラーにより関数が失敗する。

その他のシーケンスにおいても同様に発生する可能性があります。

WS2_32.DLLは、ある特定のネットワークイベントが発生するたびに、アプリケーションにメッセージを送信し続けるということはしません。ある特定のイベントの通知がアプリケーションウィンドウに正常にポストされると、そのネットワークイベントの通知を暗黙に再有効化する関数が呼び出されるまでは、するそれ以上は同じネットワークイベントに対するメッセージをポストしません。

イベント 再有効化関数
FD_READ recv, recvfrom, WSARecv, WSARecvFrom
FD_WRITE send, sendto, WSASend, WSASendTo
FD_OOB recv, recvfrom, WSARecv, WSARecvFrom
FD_ACCEPT accept, WSAAccept(エラーコードがWSATRY_AGAINである場合を除く)
FD_CONNECT なし
FD_CLOSE なし
FD_QOS コマンドSIO_GET_QOSを指定してWSAIoctl
FD_GROUP_QOS 予約されています。コマンドSIO_GET_GROUP_QOSを指定してWSAIoctl(将来のソケットグループでの使用のために予約済み)
FD_ROUTING_INTERFACE_CHANGE コマンドSIO_ROUTING_INTERFACE_CHANGEを指定してWSAIoctl
FD_ADDRESS_LIST_CHANGE コマンドSIO_ADDRESS_LIST_CHANGEを指定してWSAIoctl

いずれの再有効化関数の呼び出しでも(呼び出しが失敗した場合でも)、関係のあるイベントに対するメッセージポストが再有効化されます。

FD_READ, FD_OOB, FD_ACCEPTイベントでは、メッセージのポストはレベルトリガー方式で行われます。これは、再有効化関数が呼び出され、関係のある条件が呼び出しの後でもまだ満たされている場合には、WSAAsyncSelectメッセージがアプリケーションに通知されることを表しています。これによって、アプリケーションはイベントドリブンで動作し、どの時点でも受信したデータ量を意識する必要がなくなります。以下のようなシーケンスを考えてみましょう。

  1. ネットワーク転送スタックがソケットsへの100バイトのデータを受信することにより、Windows Sockets 2がFD_READメッセージをポストする。
  2. アプリケーションはrecv(s, buffptr, 50, 0) を呼び出して50バイトのデータを読み取る。
  3. まだ読み取り可能なデータが存在するので、再度FD_READメッセージがポストされる。

これらのセマンティックスでは、アプリケーションはFD_READメッセージを受けて、利用可能なデータをすべて読み込む必要はありません。それぞれのFD_READメッセージに対して1回のrecv呼び出しを行うのが適切な処理です。アプリケーションが1回のFD_READメッセージに対して複数回のrecv呼び出しを行うと、複数回のFD_READメッセージを受け取る可能性があります。そのようなアプリケーションでは、recv呼び出しの開始前にFD_READイベントセットせずにWSAAsyncSelect関数を呼び出すことにより、FD_READメッセージの通知を停止させることができます。

FD_QOSおよびFD_GROUP_QOSイベントはエッジトリガー方式と考えることができます。メッセージは、ちょうどサービス品質の変更が発生した時に1度だけポストされます。プロバイダが次のサービス品質の変更を検出するか、アプリケーションがソケットに対する再ネゴシエーションを行わない限り、それ以上のメッセージは通知されません。

FD_ROUTING_INTERFACE_CHANGEメッセージは、SIO_ROUTING_INTERFACE_CHANGEを指定してWSAIoctl関数を呼び出した後に、その呼び出しで指定された接続先に到達するために使用されるべきローカルインターフェースが変更された場合にポストされます。

FD_ADDRESS_LIST_CHANGEメッセージは、SIO_ADDRESS_LIST_CHANGEを指定してWSAIoctl関数を呼び出した後に、アプリケーションがバインド可能なアドレスのリストが変更された場合にポストされます。

アプリケーションがWSAAsyncSelect関数または再有効化関数を呼び出した後にいずれかのイベントが発生すると、そのイベントに応じたメッセージがポストされます。例として、以下のシーケンスを考えてみます。

  1. アプリケーションがlisten関数を呼び出す。
  2. 接続要求を受け取る。ただし、まだ接続を受け付けていない。
  3. アプリケーションがソケットに対するFD_ACCEPTメッセージを受け取るように指定してWSAAsyncSelect関数を呼び出す。イベントの持続性の結果、Windows Sockets 2は直ちにFD_ACCEPTメッセージをポストする。

FD_WRITEイベントでは若干異なった処理を行います。FD_WRITEメッセージは、ソケットがconnectまたはWSAConnect関数で最初に接続された時(もし同時にFD_CONNECTを受け取るように登録しているならばFD_CONNECTメッセージの後)、もしくは、acceptまたはWSAAccept関数で接続を受け付けた場合にポストされます。また、送信要求がWSAEWOULDBLOCKエラーで失敗し、その後でバッファが利用可能いなった際にもFD_WRITEメッセージがポストされます。したがって、アプリケーションは、最初のFD_WRITEメッセージから、送信操作がWSAEWOULDBLOCKエラーを返すまで、送信が可能であると仮定することができます。WSAEWOULDBLOCKエラーの後、アプリケーションはFD_WRITEメッセージを受け取ることで、再び送信が可能になったことを知ることができます。

FD_OOBイベントはソケットがOOBデータを別々に受け取るように構成されている場合にのみ使用されます。ソケットがOOBデータをインラインで受け取るように構成されている場合は、OOB(優先)データはひょじゅんデータと同じように扱われるため、アプリケーションはFD_OOBではなくFD_READイベントを取得するように登録し、FD_READメッセージを受け取るべきです。アプリケーションは、setsockoptおよびgetsockopt関数にSO_OOBINLINEオプションを渡すことにより、OOBデータがどのように扱われるかを設定および取得することができます。

FD_CLOSEメッセージのエラーコードは、ソケットのクローズがgracefulであったのか、abortiveであったのかを示しています。エラーコードが0である場合は、ソケットのクローズはgracefulに行われたことを示します。エラーコードが10054 (WSAECONNRESET) である場合、ソケットの仮想通信路がリセットされたことを示します。この動作は、SOCK_STREAMなどのコネクション指向のソケットにのみ適用されます。

FD_CLOSEメッセージは、ソケットに対応する仮想通信路のクローズ指示を受け取った時にポストされます。TCPに関して言えば、コネクションがTIME WAIT状態またはCLOSE WAIT状態になった時にFD_CLOSEメッセージがポストされます。これは、リモート側端末が送信側に対してshutdownを呼び出すか、またはclosesocketを呼び出したことを示しています。FD_CLOSEメッセージはすべてのデータがソケットから読み取られた後にポストされますが、アプリケーションはFD_CLOSE受信時に残りのデータがあるかどうかをチェックして、データが失われる可能性を避けるべきです。

留意すべきことは、アプリケーションは仮想通信路が閉じられたことを示すのにFD_CLOSEメッセージのみを受け取ります。それは、もしgracefulに閉じられたのであれば、受信データがすべて読み取られている場合にのみ行われます。アプリケーションは、この状態を示すためにFD_READメッセージは受け取りません。

FD_QOSまたはFD_GROUP_QOSメッセージは、それぞれ、ソケットsまたはソケットsが属するソケットグループに関連付けられたフロー仕様のいずれかのパラメータが変更された場合にポストされます。アプリケーションは、それぞれ、コマンドSIO_GET_QOSまたはSIO_GET_GROUP_QOSを指定してWSAIoctlを呼び出し、ソケットsまたはソケットsが属するソケットグループに対する現在のサービス品質を取得するべきです。

FD_ROUTING_INTERFACE_CHANGEおよびFD_ADDRESS_LIST_CHANGEイベントもまた、エッジトリガー方式と考えることができます。メッセージは、

FD_QOSおよびFD_GROUP_QOSイベントはエッジトリガー方式と考えることができます。メッセージは、それぞれ、アプリケーションがSIO_ROUTING_INTERFACE_CHANGEおよびSIO_ADDRESS_LIST_CHANGEを指定してWSAIoctl関数を呼び出して通知を要求した後で、変更が発生した場合にポストされます。アプリケーションがIOCTLの再呼び出しを行い、IOCTLが呼び出されたために別の変更が検出されない限り、それ以上のメッセージは通知されません。

それぞれの非同期通知メッセージ対するイベンと条件の要約を以下に示します。