データの送受信(標準編)対応するサーバーはこちらクライアントとサーバーが互いに接続できれば、実際にデータを送信したり受信したりできるようになります。データを送信するには、sendを呼び出します。flags);sは、接続済みソケットの記述子を指定します。bufは、送信したいデータを格納しているバッファを指定します。lenは、送信したいデータのサイズを指定します。flagsは、送信方法について定義されている定数を指定します。基本的には、0を指定します。戻り値は、送信されたデータのサイズが返ります。エラーが発生した場合は、SOCKET_ERRORが返ります。送信されたデータを受信するには、recvを呼び出します。flags);sは、接続済みソケットの記述子を指定します。bufは、受信したデータを格納するバッファを指定します。lenは、bufのサイズを指定します。flagsは、受信方法について定義されている定数を指定します。基本的には、0を指定します。戻り値は、受信したデータのサイズが返ります。相手が接続を閉じた場合は、0が返ります。エラーが発生した場合は、SOCKET_ERRORが返ります。ソケットデータの送受信に使用した場合は、この関数の前にshutdownを呼び出すことになります。この関数は、データの送受信の片方または両方をできなくします。how);sは、接続済みソケットの記述子を指定します。howは、無効にする種類を表す定数を指定します。SD_SENDは送信を無効にし、SD_RECEIVEは受信を無効にします。SD_BOTHを指定すると、送信と受信共に無効になります。次に、sendとrecvの呼び出し方の例を示します。sendを呼び出してサーバーにデータを送信することができます。この文字列がサーバーに送信されることになります。サーバーから受信するためです。サーバーがsendを呼び出してそのデータがクライアントに届いてからとなります。recvのような、目的の動作が完了するまで制御を返さない仕組みは、ブロッキングと呼ばれています。GUIアプリケーションなどにとっては不都合になることがあります。recvで待機している間は他のメッセージを処理することができなくなるという問題があります。これを防ぐには、新しくスレッドを作成し、そこでrecvを呼び出す方法が考えられます。TEXT("サーバーとの接続が切断されました。TEXT("%dバイト受信しました。0;}新しく作成されたスレッドはこの関数内でrecvを呼び出し、受信したデータを表示します。基本的にスレッドはrecvによって待機した状態にいますから、CPU使用率が上がるようなこともなくなります。上記コードには全くの問題がないわけではありません。スレッドがrecvで待機している場合は、この変数の値が確認されることはありません。g_bExitThreadをTRUEにしても、スレッドが直ちにループから抜けられるとは限りません。リソースを適切に開放するという点などにおいて、できればスレッドは自分自身で終了するべきといえます。上記の問題を解消するためには、ソケットのブロッキングモードを変更する方法があります。別スレッドによって変更された変数を確認できるようになります。ブロッキングモードは、ioctlsocketで変更することができます。*argp);sは、ソケットの記述子を指定します。cmdは、ソケットに設定したいコマンドの種類を表す定数を指定します。ブロッキングモードを変更する場合は、FIONBIOを指定します。argpは、設定したい値を格納している変数のアドレスを指定します。recvは直ちに制御を返すようになります。今回のクライアントプログラムは、対応するサーバープログラムにデータを送信します。クライアントはこれを別スレッドで受信します。事前に、対応するサーバープログラムを起動しておいてください。TEXT("メッセージを入力してください。TEXT("データの送信に失敗しました。TEXT("サーバーとの接続が切断されました。TEXT("%dバイト受信しました。TEXT("ホスト情報からアドレスの取得に失敗しました。TEXT("サーバーがリッスンしていません。TEXT("サーバーが存在しません。TEXT("サーバーへの接続が失敗しました。引数で指定されたサーバーのポートへ接続を行います。この関数が成功した場合は、サーバーとの通信を行うことができます。CreateThreadを呼び出しているのは、サーバーからのデータを受信するためのスレッドを作成するためです。recvの呼び出しにはソケットが必要になるため、第4引数にソケットを指定することで第3引数の関数に渡そうとしています。その文字列がサーバーに送信されることになります。この処理は、WM_COMMANDで行われています。TEXT("データの送信に失敗しました。0;}ボタンの識別子はID_SENDに設定しているため、これ以外の理由でWM_COMMANDが送られた場合はreturnするようにしています。その文字数を戻り値から取得します。続いて、文字数に'\0'文字を含めるために+1をし、サイズに変換するためにsizeof(TCHAR)を掛けます。sendを呼び出すことでサーバーへ送信します。ThreadProcは、CreateThreadによって作成されたスレッドが実行することになります。この関数では、recvを呼び出してサーバーからデータを受信します。TEXT("サーバーとの接続が切断されました。TEXT("%dバイト受信しました。0;}まず、ioctlsocketにFIONBIOと1を指定することで、ソケットを非ブロッキングモードに設定します。これにより、recvはサーバーからデータを届いてない場合でも制御を返すことになります。Sleepを呼び出すことで指定されたミリ秒だけ待機するようにしています。recvを呼び出すようにしています。することで、スレッドが終了するべきかどうかを確認できるようになります。データを受信できた場合は0以上の値を返すことになっています。よって、この場合は受信したデータを表示することができます。これ以上サーバーからデータが送信されることはありません。よって、ループをbreakすることになります。最後に、メインスレッドが実行するWM_DESTROYの処理を確認します。1000);CloseHandle(hThread);}g_bExitThreadをTRUEにすることにより、スレッドは自身が終了すべきタイミングを知ることができます。タイムアウトによって関数が制御を返すことはないはずです。それに伴ってWaitForSingleObjectも制御を返すことになります。引数の文字列がchar型であるという欠点があります。できれば全ての文字列はTCHAR型として扱うのが理想といえます。これを呼び出す例を示します。TEXT("ホスト情報からアドレスの取得に失敗しました。TEXT("サーバーがリッスンしていません。TEXT("サーバーが存在しません。TEXT("サーバーへの接続が失敗しました。soc;}GetAddrInfoExを呼び出す場合は、ADDRINFO構造体ではなくADDRINFOEX構造体を呼び出します。取得したアドレスを開放する関数は、FreeAddrInfoExになります。NS_DNSを指定した場合はDNSを通じてアドレスを取得しようとします。第4引数は、明示的に参照したいネームサービスプロバイダーのGUIDを指定しますが、NULLで問題ありません。pAddrList以降の引数については、現在はまだ使用されないためNULLを指定します。