WSAAsyncSelect関数は、ソケットに対してWindowsメッセージによるネットワークイベント通知を要求します。
int WSAAsyncSelect( SOCKET s, // socket discriptor HWND hWnd, // window handle unsigned int wMsg, // message code long lEvent // bitmask );
WS2_32.DLL
イベント通知を要求するソケットを識別するディスクリプタを指定します。
ネットワークイベントが発生した際にメッセージを受信するウィンドウを識別するハンドルを指定します。
ネットワークイベントが発生した際に受信するメッセージを指定します。
アプリケーションが関心のあるネットワークイベントの組み合わせを示すビットマスクを指定します。以下の表に示す値のビット論理和で指定します。
読み取り可能となったことの通知を受け取るように設定します。
書き込み可能となったことの通知を受け取るように設定します。
OOBデータ到着の通知を受け取るように設定します。
外部からの接続の通知を受け取るように設定します。
コネクション確立またはマルチポイントjoin操作の通知を受け取るように設定します。
ソケットが閉じられたことの通知を受け取るように設定します。
ソケットのサービス品質(QOS)変更の通知を受け取るように設定します。
ソケットグループのサービス品質(QOS)の変更の通知を受け取るように設定します。
指定された接続先へのルーティングインターフェース変更の通知を受け取るように設定します。
ソケットプロトコルファミリに対するローカルアドレスリスト変更の通知を受け取るように設定します。
このパラメータに0を指定すると、すべての通知をキャンセルしてWindows Socketsがソケットに関するネットワークイベントのメッセージをこれ以上送信しないようにします。
成功すると、アプリケーションのネットワークイベントセットへの関心の宣言が成功したならば、戻り値は0になります。
失敗すると-1 (SOCKET_ERROR) を返します。特定のエラーコードを取得するにはWSAGetLastError関数を呼び出します。
ウィンドウハンドルが既存のウィンドウを参照していなかったり、指定されたソケットが有効な状態ではない等、いずれかのパラメータが有効ではありません。
ブロッキングWindows Sockets 1.1呼び出しが実行中であるか、サービスプロバイダがコールバック関数を実行中です。
指定されたディスクリプタはソケットではありません。
ネットワークサブシステムが失敗しました。
この関数を呼び出す前にWSAStartup関数の呼び出しが成功していなければなりません。
アプリケーションウィンドウがメッセージを受け取る際に、追加のエラーコードがセットされる可能性があります。このエラーコードは応答メッセージのlParamパラメータからWSAGETSELECTERRORマクロ(上位ワードを取り出す)で取り出すことができます。それぞれのネットワークイベントに対して起こりうるエラーコードを以下の表に示します。
namelenパラメータの値が無効です。
ソケットはすでにアドレスにバインドされています。
これ以上のソケットディスクリプタを使用することはできません。
指定されたファミリのアドレスをこのソケットで使用することはできません。
現時点でこのホストからネットワークに到達することができません。
利用可能なバッファ空間がありません。ソケットを接続できません。
指定されたソケットはすでに接続されています(コネクション指向のソケットのみ)。
ソケットは接続されていません。
接続試行はコネクションが確立することなくタイムアウトしました。
接続の試行が拒否されました。
ネットワークサブシステムが失敗しました。
コネクションはタイムアウトまたは他のエラーにより終了しました。
リモート側によりコネクションがリセットされました。
ネットワークサブシステムが失敗しました。
指定された接続先はすでに到達不能です。
ネットワークサブシステムが失敗しました。
WSAAsyncSelect関数は、lEventパラメータで指定されたいずれかのネットワークイベントを検出した時に、WS2_32.DLLがウィンドウhWndにメッセージを送信するように要求するために使用します。送信されるメッセージをwMsgパラメータで、通知が必要なソケットをsパラメータで指定します。
WSAAsyncSelect関数は、lEventの値に関わらずソケットsを自動的に非ブロッキングモード設定します。ソケットsをブロッキングモードに戻すには、まずlEventに0を指定してWSAAsyncSelect関数を呼び出し、ソケットsに関連付けられているイベントレコードをクリアする必要があります。その後、ioctlsocketまたはWSAIoctl関数を呼び出してソケットをブロッキングモードに戻すことができます。
WSAAsyncSelect関数を発行すると、それまでの同じソケットに対するWSAAsyncSelectまたはWSAEventSelect呼び出しはキャンセルされます。例えば、呼び出しおよび書き込みの両方に対する通知を受け取るには、アプリケーションはFD_READとFD_WRITEの両方を指定してWSAAsyncSelectを呼び出さなければいけません。
すべての通知をキャンセルしてWindows Socketsがソケットに関するネットワークイベントのメッセージをこれ以上送信しないようにするには、lEventに0を指定します。
この場合、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パラメータの上位ワードが示すエラー値と異なる可能性があります。
発生する可能性のあるネットワークイベントコードを以下の表に示します。
ソケットsが読み取り可能です。
ソケットsが書き込み可能です。
ソケットs上のOOBデータが読み取り可能です。
ソケットsが外部からの新しい接続を受け取ることが可能です。
ソケットsで開始されたコネクションが確立またはマルチポイントjoin操作が完了しました。
ソケットsで識別されるコネクションが閉じられました。
ソケットsに関連付けられているサービス品質(QOS)が変更されました。
この値は予約されています。ソケットsが属するソケットグループのサービス品質(QOS)が変更されました。(将来のソケットグループでの使用のために予約済み)
指定された接続先へ送信するために使用されるローカルインターフェースが変更されました。
アプリケーションクライアントがバインド可能なソケットプロトコルファミリのローカルアドレスのリストが変更されました。
複数のイベントの監視のためにWSAAsyncSelect関数を呼び出すことができますが、アプリケーションウィンドウはそれぞれのネットワークイベントごとに1つのメッセージを受け取ります。
select関数の場合と同様に、WSAAsyncSelect関数は、データ転送操作(sendやrecv)が直ちに成功することを期待して呼び出せるタイミングを決定するために使用されます。ただし、堅牢なアプリケーションでは、メッセージを受け取ってWindows Sockets 2呼び出しを発行した場合でも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メッセージがアプリケーションに通知されることを表しています。これによって、アプリケーションはイベントドリブンで動作し、どの時点でも受信したデータ量を意識する必要がなくなります。以下のようなシーケンスを考えてみましょう。
これらのセマンティックスでは、アプリケーションは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関数または再有効化関数を呼び出した後にいずれかのイベントが発生すると、そのイベントに応じたメッセージがポストされます。例として、以下のシーケンスを考えてみます。
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が呼び出されたために別の変更が検出されない限り、それ以上のメッセージは通知されません。
それぞれの非同期通知メッセージ対するイベンと条件の要約を以下に示します。