Windows用のTCPソケットブロックモードの定義

TCPソケットを扱う人は、ソケットがブロックモードまたは非ブロック(非ブロック)モードで動作できることを知っています。 作成後、Windowsソケットはブロッキングモードになりますが、非ブロッキング関数ioctlsocket()に変更できます。



1つのプロジェクトで作業するとき、DLL関数によって提供されるソケットがどのモードで機能するかを判断するタスクがありました。 すなわち ソケット自体を除いて、私には情報がなく、それらは予測不可能なモードになりました。



* nixでは、ブロックモードはfcntl()関数を呼び出すことで問題なく定義されていますが、WinSock2では種類が見つからず、フォーラムでは「Windowsは現在ソケットがブロッキングまたは非ブロッキングに設定されているかどうかを照会する方法を提供していません」誰も答えなかった。



しかし、まだ決定する方法があります:



// GetBlockingMode : 1 - nonblocking | 0 - blocking | -1 - error | -2 - timeout reseted! int GetBlockingMode(int Sock) { int iSize, iValOld, iValNew, retgso; iSize = sizeof(iValOld); retgso = getsockopt(Sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&iValOld, &iSize); // Save current timeout value if (retgso == SOCKET_ERROR) return (-1); iValNew = 1; retgso = setsockopt(Sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&iValNew, iSize); // Set new timeout to 1 ms if (retgso == SOCKET_ERROR) return (-1); // Ok! Try read 0 bytes. char buf[1]; // 1 - why not :) int retrcv = recv(Sock, buf, 0, MSG_OOB); // try read MSG_OOB int werr = WSAGetLastError(); retgso = setsockopt(Sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&iValOld, iSize); // Set timeout to initial value if (retgso == SOCKET_ERROR) return (-2); if (werr == WSAENOTCONN) return (-1); if (werr == WSAEWOULDBLOCK) return 1; return 0; }
      
      





この関数はソケット番号を受け入れ、ソケットが非ブロッキングモードの場合は1、ブロックエラー、検出エラーが発生した場合は-1、モード定義後のソケットに小さなタイムアウトが残っている場合は-2を返します。



簡単に説明すると、アクションのシーケンスは次のとおりです。



1.ソケットのタイムアウト値を保存します(デフォルトでは0-「永遠に待機」)。

2.タイムアウトを1ミリ秒に設定します。

3.ソケットから帯域外データの0(ゼロ)バイトを読み取ります。 説明が必要です:OOBデータが送信された場合、関数は嘘をつくことができますが、これを受け入れるときにWindows NT4がブルースクリーンにクラッシュしたため、OOBに遭遇したことがありません( WinNuke )。

4.読み取りの結果としてエラーが発生しました。

5.古いソケットタイムアウト値を復元します。

6.読み取りエラーの種類を確認します。WSAEWOULDBLOCKの場合、ソケットは非ブロックモードになっており、これは決定されていました。



既に述べたMSG_OOBに加えて、この方法の欠点には、1ミリ秒の遅延と、ソケットの読み取りタイムアウトを損なうゼロ以外の確率が含まれます(ただし、負荷テストではこのような動作は明らかになりませんでした)。



All Articles