Windows ソケット 1.1 ブロッキング ルーチンと EINPROGRESS
Berkeley Sockets 環境から Windows 環境へのアプリケーションの移植に関する主な問題の 1 つは、ブロックです。つまり、関連付けられた操作が完了するまで戻らない関数を呼び出します。 操作の完了に任意の時間がかかる場合に問題が発生します。たとえば 、recv 関数です。これは、ピア システムからデータを受信するまでブロックする可能性があります。 Berkeley Sockets モデル内の既定の動作は、プログラマが操作を非ブロッキングとして明示的に要求しない限り、ソケットがブロッキング モードで動作することです。 Windows Sockets 1.1 環境では、プリエンプティブ スケジューリングを想定できませんでした。 したがって、プログラマは、Windows Sockets 1.1 で可能であれば、非ブロッキング (非同期) 操作を使用することを強くお勧めします。 これは必ずしも可能とは限らないので、以下に記載の擬似ブロッキング設備が提供された。
Note
Windows ソケット 2 は、デッドロックが問題ではないプリエンプティブ 32 ビット オペレーティング システムでのみ実行されます。 Windows ソケット 1.1 に推奨されるプログラミングプラクティスは、Windows ソケット 2 では必要ありません。
ブロッキング ソケットでも、 バインド、 getsockopt、 getpeername などの一部の関数がすぐに完了します。 これらの関数のブロッキング操作と非ブロッキング操作には違いはありません。 recv などの他の操作は、さまざまなトランスポート条件に応じて、すぐに完了するか、任意の時間を取って完了できます。 ブロッキング ソケットに適用すると、これらの操作はブロッキング操作と呼ばれます。 次の関数はブロックできます。
16 ビット Windows ソケット 1.1 では、すぐには完了できないブロック操作は、次のように擬似ブロッキングによって処理されます。
サービス プロバイダーは操作を開始し、Windows メッセージをディスパッチするループに入り (必要に応じてプロセッサを別のスレッドに出力する)、Windows Sockets 関数の完了を確認します。 関数が完了した場合、または WSACancelBlockingCall が呼び出された場合、ブロック関数は適切な結果で完了します。
サービス プロバイダーは、ブロック操作が未処理の間にメッセージを再入可能にする可能性を回避するために、メッセージを処理しないブロッキング フック関数のインストールを許可する必要があります。 このような最も単純なブロッキング フック関数は FALSE を返します。 Windows ソケット DLL が内部操作のメッセージに依存している場合は、アプリケーション ブロック フックを実行する前に PeekMessage(hMyWnd...) を実行して、システムの残りの部分に影響を与えることなくメッセージを取得できるようにします。
16 ビット Windows ソケット 1.1 環境では、ブロック操作が進行中のプロセスに対して Windows メッセージを受信すると、アプリケーションが別の Windows ソケット呼び出しを発行しようとするリスクがあります。 この状態を安全に管理するのが難しいため、Windows Sockets 1.1 ではこのようなアプリケーション動作はサポートされていません。 入れ子になった複数の Windows ソケット関数呼び出しを行うアプリケーションは許可されていません。 特定のタスクに対して許可される未処理の関数呼び出しは 1 つだけです。 唯一の例外は、この状況でプログラマを支援するために提供される 2 つの関数です。 WSAIsBlocking と WSACancelBlockingCall。
WSAIsBlocking 関数は、ブロックしている Windows ソケット 1.1 呼び出しが進行中かどうかを判断するために、いつでも呼び出すことができます。 同様に、 WSACancelBlockingCall 関数をいつでも呼び出して、進行中のブロック呼び出しを取り消すことができます。 Windows ソケット関数のその他の入れ子は、エラー WSAEINPROGRESS で失敗します。
この制限は、ブロック操作と非ブロッキング操作の両方に適用されることを強調する必要があります。 WSAStartup の呼び出し時にバージョン 2.0 以降をネゴシエートする Windows ソケット 2 アプリケーションの場合、操作の入れ子に関する制限は終了しません。 WSAAccept の条件付き受け入れコールバック中や、サービス プロバイダーが Windows ソケット 2 関数を呼び出す場合など、まれに操作が入れ子になる可能性があります。
このメカニズムは単純なアプリケーションには十分ですが、より高度なアプリケーション (MDI モデルを使用するアプリケーションなど) の複雑なメッセージディスパッチ要件をサポートすることはできません。 このようなアプリケーションの場合、Windows Sockets API には WSASetBlockingHook 関数が含まれています。これにより、前の説明で説明した既定のメッセージ ディスパッチ ルーチンの代わりに呼び出すことができる特別なルーチンをアプリケーションで指定できます。
Windows ソケット プロバイダーは、次のすべてが当てはまる場合にのみブロック フックを呼び出します。
- ルーチンは、ブロック可能として定義されているルーチンです。
- 指定されたソケットはブロッキング ソケットです。
- 要求をすぐに完了することはできません。
ソケットは既定でブロッキングに設定されていますが、FIONBIO IOCTL または WSAAsyncSelect 関数を使用する ioctlsocket 関数では、ソケットを非ブロッキング モードに設定できます。
ブロッキング フックは呼び出されず、アプリケーションが次のガイドラインに従っている場合にブロック フックによって発生する可能性がある再エントランシーの問題に関する必要はありません。
- 非ブロッキング ソケットのみを使用します。
- select ルーチンと getXbyY ルーチンではなく、WSAAsyncSelect ルーチンまたは WSAAsyncGetXByY ルーチンを使用します。
Windows Sockets 1.1 アプリケーションが、メモリ オブジェクト (バッファーやグローバル変数など) へのポインターを引数として受け取る非同期操作または非ブロック操作を呼び出す場合、そのオブジェクトが操作全体を通じて Windows Sockets で使用できることを確認するのはアプリケーションの責任です。 アプリケーションは、関連するメモリのマッピングまたはアドレスの実行可能性に影響を与える可能性のある Windows 関数を呼び出してはなりません。