winsock2でクローズの処理で迷う。

サーバソケットを作っていてふと、通信を受信後、ソケットのクローズに失敗する場合に遭遇した。
というか、連続して、たくさんSocket通信を投げつける、
例えば100回のTCPの送信を5秒ぐらいで送るような負荷をかけると、
プログラムがいきなり死んだ。どうやら、ワトソンのログは出ていたので、
調べてみる、どうやら、shutdownの処理で失敗した直後に死ぬらしい。
こういう処理になっていた、と、思う。


// リッスンソケットは既に作成済み
WSAAsyncSelect(hoge, hoge, hoge, FD_ACCEPT | FD_READ | FD_CLOSE);
//
// メッセージループ
switch (メッセージ){
case FD_ACCEPT:
s = accept // 受信ソケットを作る
break;
case FD_READ:
if(recv(s,buf,sizeof(buf),0)) == receive_size){
// 受信後の処理
char * p = new char[sizeof(receive_size)];
AfxBeginThread(receiveFunc, p);
}
break;
case FD_CLOSE:
shutdown(s, SD_BOTH); // ここで失敗する、時がある
closesocket(s);
break;
}
UINT receiveFunc(LPVOID param)
{
char * p = (char *)param;
// 受信文字のごちゃごちゃした処理
delete [] p;
}
記憶を頼りに書いているので、多少間違いはあるかもしれないけれど、
大体こんな感じの処理だった、と思う。
で、問題は、処理が輻輳するとshutdownで失敗が上がるらしい。
ここで、しばらく考える。
shutdown関数は失敗するとSOCKET_ERRORを返すので、失敗の場合を見つけるのは
簡単だが、簡単なのだが、さて、そこで、何か処理をしたところで、
shutdownが成功するものなのだろうか?
e_c_e_tのこれまでの理解だと、切断の手順は

1.ClientがshutdownをServerに宣告
(ServerでFD_CLOSEが挙がる)
2.Serverでshutdownをして、Clientへもう通信しないことを伝える
(Clientのshutdownの制御が返る)
3.Clientがclosesocketを実施
4.Serverがclosesocketを実施
と、するのが礼儀正しい切断のやり方、だと、理解しているのだが、
Server側でshutdownが失敗する場合というのは、どういうことだろう?
Clientの反応が遅いだけ?
よーし、ループで10回ぐらい繰り返してみよう
 → 10回連続で失敗する。一度失敗すると、ダメ?
相手が、いきなりclosesocketを叩いていて、shutdownが応答できない、とか?
shutdownで失敗した場合のclosesocketへ導く手段が見つからない。
リソースが浮いてしまうのはイヤだけど、そんなに多く失敗するわけでは
ないのだから、それは、無視しよう。ワトソン吐いて落ちるよりは遥かにマシだ。
とか、考えて、紆余曲折を経てソースは次のようになった。

// リッスンソケットは既に作成済み
WSAAsyncSelect(hoge, hoge, hoge, FD_ACCEPT | FD_READ);
//
// メッセージループ
switch (メッセージ){
case FD_ACCEPT:
s = accept // 受信ソケットを作る
break;
case FD_READ:
if(recv(s,buf,sizeof(buf),0)) == receive_size){
// 受信後の処理
char * p = new char[sizeof(receive_size)];
AfxBeginThread(receiveFunc, p);
}
shutdown(s, SD_BOTH);
closesocket(s);
break;
}
windowMessageのやり取りに頼る、この方式では1秒に20回の通信とか、が、
上限になってしまう。
シビアなタイミングが要求される場合は、windowMessageに頼らず、acceptするまで待つような専用のスレッドを起こす必要がある、んだろう。
たぶん。。。