非同期ソケット通信、簡易チャットプログラム -004-
前へ TOPへ 次へ

非同期ソケット通信

これまで説明してきたソケット関数、具体的にはaccept、connect、recv、send、closesocketの各関数は基本的にアクションが完了するまで処理が戻ってきません。 たとえば、接続先が遠いところにある場合など相手からなかなか応答が来ないとき、関数を呼び出したルーチンはずっとブロックされ、相手側から応答があるか何らかのエラーが検出されない限り何もできなくなる状態になります。 こうなるとウィンドウの移動や再描画などのウィンドウメッセージをまったく処理できなくなります(アプリケーションの終了すらできない)。 これでは使い易いプログラムとは言えません。 これらの関数群はUNIX版のソケット用APIをそのまま移植したもので、同期型APIといいます。

このような状態を回避するため、Windows Socketではノンブロッキング処理(非同期処理)ができるような関数群、つまり非同期型APIが用意されています。 非同期型APIは上記のような同期型APIの使いづらさを克服するためにMicrosoftが独自に開発したものです。 非同期型APIは呼び出されるとすぐに制御を戻します。そして処理が完了した時に、呼び出し側のウィンドウに個別のメッセージを送って来ます。 呼び出し側はこのメッセージが返って来るまで他の処理を行えるという利点があります。 このようなやり方は、Windowsのメッセージ処理プログラミングに慣れてくるととても便利に感じます。

具体的に非同期処理を行うためには、send()、recv()、connect()、accept()など、通常は相手からのレスポンスがあるまでブロッキングしてしまうようなソケット関連の関数をコールする直前に非同期型APIのWSAAsyncSelect()を呼びます。 こうすると 非同期処理モードに入ります。この状況下でのソケット関数の呼び出しは即座に戻り(制御がすぐ返される)、実際にアクションが完了したときには第3引数で指定した通知メッセージが発行されます。 従ってアプリケーションプログラム側では、

  1. WSAAsyncSelect()関数の呼び出し
  2. ブロッキング関数(例えばrecv関数)の呼び出し
  3. 一旦処理をOS側に返し
  4. 定義した通知メッセージ(例えばWM_SOCKET)で待ち,通知内容がFD_READであれば相手からのメッセージが到着しているはずなので取り込み処理等の手順を行う

というようにすればよいわけです。

このやりかたについて次に詳しくみていきます。 それにはまず非同期通信用WinSockのAPIを初期化するWSAStartup関数と、非同期モードに設定するWSAAsyncSelect関数、そしてWinSock のリソースを開放するWSACleanup関数を知る必要があります。

WinSock APIを初期化する

非同期モードに入る(ソケットイベントをウィンドウメッセージで通知してもらうようにする)

WSAAsyncSelect関数は、指定のソケットの接続や送信や受信や切断などのイベントを、ユーザが指定したウィンドウメッセージで通知させるようにする関数です。
WSAAsyncSelect関数の第1引数は,ウィンドウメッセージ通知の対象となるソケットです。
第2引数は,ソケットイベントのウィンドウメッセージを受け取るウィンドウのハンドルです。
第3引数は,通知するメッセージです。
第4引数は,通知させるイベントのタイプです(下記)。
通知内容が

  1. FD_ACCEPTなら,相手クライアントから接続要求が届いていることを通知します。
  2. FD_CONNECTなら,ソケットへの接続が完了したことを通知します。
  3. FD_WRITEは,ソケットの送信バッファに空きができたことを通知します。
  4. FD_READは,ソケットの受信バッファにデータがあることを通知します。
  5. FD_CLOSEは,ソケットへの接続が終了したことを通知します。

戻り値が 0 の場合は成功で,SOCKET_ERRORの場合はエラーです。

複数のソケットを扱う場合は,ソケット毎にWSAAsyncSelect関数を実行します。 同一ソケットに対して複数回WSAAsyncSelect関数を実行した場合、最後に実行したWSAAsyncSelect関数だけ有効になりそれ以前のものは無効になります。

この関数の使い方を,それぞれの場合について説明します。イベント内容ごとに下記の使い方ができます。

FD_ACCEPT
クライアント側からのconnectを待つときに、サーバ側で行うaccept関数のブロッキングを防ぐために用いる。 この関数を呼び出した後、第3引数で指定したWM_XXXXメッセージが相手からのconnect受信のときに発行される。 従って、WM_XXXXメッセージでイベント内容がFD_ACCEPTのときにaccept関数を呼び出すようにすれば結果的にノンブロッキング呼び出しとして使える。
FD_CONNECT
クライアントがサーバに対して接続するときに呼び出すconnect関数のブロッキングを防ぐのに用いる。 この関数を呼び出した直後にconnect関数を呼び出せば接続要求を発行するだけでブロッキングされることは無い。 接続が完了した時点で、第3引数で指定したWM_XXXXメッセージがイベント内容=FD_CONNECTとして発行される。
FD_WRITE
ソケットを通してデータを送信するとき、同期通信方式では相手が受信状態になるまでブロックされてしまう。 そこでこの関数WSAAsync...を呼び出した直後に送信命令を発行しておけば(相手側の受信を待たずに)ノンブロッキング呼び出しとなってリターンされる。 その後相手の受信時に、関数の第3引数で指定したWM_XXXXメッセージがイベント内容=FD_WRITEとして発行されるので,送信の完了が確認できる。
FD_READ
ソケットを通してデータを受信するとき、(データ送信のときと同様)同期通信方式では相手が送信状態になるまでブロックされる。 そこでこの関数WSAAsync...を呼び出した直後に受信命令を発行しておけば(相手の送信を待たずに)ノンブロッキング呼び出しとなってリターンされる。 その後相手の送信時に、関数の第3引数で指定したWM_XXXXメッセージが通知されイベント内容=FD_READとしてやって来るので,受信の完了が確認できる。
FD_CLOSE
相手端末から切断要求(closesocket関数による)が来た場合にこの関数の第3引数で指定したWM_XXXXメッセージがイベント内容=FD_CLOSEとして発行される。 これにより相手の切断要求の受信ならびに回線の切断が確認される。

つまり,ソケットに状態の変化が起きたときにはイベント「WM_XXXX」で通知してもらうということを予約するのがこの関数です。 その使用方法は以下のようになります。

WinSock APIを初期化する

変数型名

Winsockではソケット通信で使用される各変数型を、Windowsの命名規約に合わせるように定義し直してます。 従来の冗長な変数型とWindowsの命名規約のどちらを利用するかは自由です。 UNIXへの移植を考慮するのなら、UNIXのsocket.hで定義されている従来の変数型を用いる方がいいかもしれません。

標準型再定義型説明
struct sockaddrSOCKADDRソケットアドレス
struct sockaddr_inSOCKADDR_INインターネット用ソケットアドレス
struct hostentHOSTENTホスト情報

前へ TOPへ 次へ