[Winsock] non blocking socketをconnect()する

WSAEWOULDBLOCKは non-blocking socketへのアクセス(たとえば、readやconnect)が成功しない状態のときにWSAGetLastError()から返る。read()が呼ばれた時にデータないときや、socketの接続が終了していない状態でconnectが抜けたときにはこの状態になる。

Windows 2000 Serverではこのエラーにからんで以下のhotfixが出ている。
Socket applications quit with a WSAEWOULDBLOCK error in Windows 2000 Server
http://support.microsoft.com/kb/839045

Windows Vista SP2上でソフトウェアの動作を確認しているときに、non blocking socketで上記のKBと同じような現象を経験した。
non-blocking socketでconnect()を呼び出してselectから抜けてきた状態でのfdをチェックすると、WSAEWOULDBLOCKが返される、というもの。

selectは何らかのイベントが発生したときに返ってくるものだから、WSAEWOULDBLOCKは返らないはずだ。
上記のhotfixはWindows 2000 ServerのものでありWSAEventSelect()を使っている自分の手元で発生しているのはselect()を使っているので原因は別だろう。nonblocking socketはもともと互換性の少ない部分であることは確か。

手元のコードをWindows 7で実行してみると現象が発生しないのでバグが修正されたのだとも考えられるし、OSの違いともいえそう。このあたりが微妙なのがnon blocking socketのイヤなところだ。 とりあえず、Vista対策ということでWSAEWOULDBLOCKは無視する(本来そこではこのエラーは出ないはずなので)ことにした。ちょっと汚いけどしょうがない。

追記。
WSAGetLastError()の結果にWSAEWOULDBLOCKが返ってくる問題に遭遇、と書いた。
その後、同じコードで作業していると定義されていないコードが返ってくる、という別の現象が発生した。
振る舞いがおかしいので使い方の問題かと思い、ドキュメントを読むと以下のように書いてある。

WSAGetLastError
  http://support.microsoft.com/kb/839045

A successful function call, or a call to WSAGetLastError, does not reset the error code. To reset the error code, use the WSASetLastError function call with iError set to zero. A getsockopt SO_ERROR also resets the error code to zero.

関数呼び出しが成功しても、WSAGetlastErrorの呼び出しでもエラーコードはリセットされない。リセットするには、WSASetLastErrorでゼロをセットする。getsockoptのSO_ERRORでもエラーコードをリセットする。
だから以前のエラーが見えている、ということだったのだろう。
WSAGetLastError()はスレッド単位でエラーを返すのに対して、getsockoptはソケットに対するエラーを返す(スレッドによらない)、という違いがある。

広告
%d人のブロガーが「いいね」をつけました。