お手伝いの関係でWinsock2を触り始めているが、MSDN掲載の初歩のサンプルコードを打ち込んでみたら妙な挙動になって2時間ほど嵌ってしまった。 - "MSDN Library" > "Windows Development" > "Networking" > "Network Protocols" > "Windows Sockets 2" > "Using Winsock" > "Getting Started With Winsock" > "Running the Winsock Client and Server Code Sample" > "Complete Winsock Client Code" このサンプルコードでは send() 後に shutdown(SOCKET, SD_SEND) して「もうこれ以上クライアント側からの送信データはありませんよ」とサーバ側に明示した後、recv()で受信し、closesocket()を呼んでいる。 shutdown()は送信 or 受信 or 両方についてソケットを無効化する。もちろんPOSIXでも定義されているが、あまり見かけることはない。 一旦shutdown()を明示的に呼んでソケットを無効化したのち、recv() or send() で残りのデータ送受信処理を行い、closeするというのはMSDN上では「Graceful Shutdown」と呼ばれているらしい。 - "MSDN Library" > "Windows Development" > "Networking" > "Network Protocols" > "Windows Sockets 2" > "About Winsock" > "Winsock Programming Considerations" > "Graceful Shutdown, Linger Options, and Socket Closure" あまり見かけることがないのは、closesokcet()内で暗黙的にgraceful shutdownが呼ばれている為、明示的にshutdown()を呼ばなくても問題ないケースが多い為だろう。 ここからが本題だが、client-sideでの「send() -> shutdown(SD_SEND) -> recv()」で、recv()がいきなり0 = connection closeを返す場合がある。 実験してみたところ、 ''サーバー側が先にFINパケットを送信すると、recv()が0を返す'' 挙動を見つけた。 例えばJavaで作った単純な"echo"サーバーに接続したところ、問題なくrecv()は"echo"サーバーが送信してきたデータを受信出来た。 ところが、送信データを"GET / HTTP.1.0"にしてローカルのApache等に送信してみたところ、かならずrecv()が0を返してしまう。こちらのケースは、Apacheプロセス側が応答パケットの送信に続いてFINパケットまで送ってきている。つまりサーバー側から先に切断フェーズに進む形になる。どうもこのケースにおいては、クライアント側でshutdown(SD_SEND) -> recv() すると0が返されconnection closeとして動作するようだ。 更に、試しにクライアント側でshutdown()の前に適当なwaitを入れてみたところ(getchar())、その場合はrecv()が正常値を返すことを確認出来た。 前述の「graceful shutdown」MSDNでは「クライアント側からの切断」処理については書かれているが、「サーバー側が先に切断フェーズに入った場合」の挙動については書かれていない。 あまりまとまっていないが、shutdown()を明示的に呼び出すgraceful shutdownで期待したとおりの挙動にならない場合は、どちらが先にFINパケットを送り切断フェーズに入ったのかについて注意が必要。