0

単純なwinsockクライアント/サーバーアプリケーションがあります。ほとんどすべてが正常に機能しますが、クライアントアプリケーションが終了してもrecvが値を返さない場合があります。

MSDNからの引用:

エラーが発生しない場合、recvは受信したバイト数を返し、bufパラメーターが指すバッファーには受信したこのデータが含まれます。接続が正常に閉じられている場合、戻り値はゼロです。それ以外の場合は、SOCKET_ERRORの値が返され、WSAGetLastErrorを呼び出すことで特定のエラーコードを取得できます。

そのrecvがクライアントに接続せずに二度と戻らず、永久にハングする理由は何でしょうか?

関連するサーバーコード:

const
  BUFSIZE = 512;
var
  Sock: TSocket;
  I   : Integer;
  Buf : AnsiString;
begin
  repeat
    SetLength(Buf, BUFSIZE);
    //blocking call
    I := recv(Sock, Pointer(Buf)^, BUFSIZE, 0);
    if I > 0 then
    begin
      SetLength(Buf, I);
      //do s.th. with Buf
    end;
  until I <= 0; //Connection closed or error

  //Sometimes never here

  Synchronize(procedure
  begin
    FOnConnectionClosed(Self, Sock, WSAGetLastError);
  end);
end.
4

2 に答える 2

1

recv()FINグレースフルディスコネクト(パケットを送信するディスコネクト)でブロックされることはありません。したがって、実際には正常な切断ではないか、読み取りコードがソケットと同期しておらず、読み取り元と思われるソケットから実際に読み取りを行っていません。

于 2012-10-02T00:08:05.650 に答える
0

「接続が正常に閉じられた場合」 - 正常に閉じられていない場合 (誰かがクライアントでネットワーク ケーブルを抜いた場合)、サーバーの recv() 呼び出しは待機し続けます。KEEPALIVE ソケット オプションを設定できますが、デフォルトではハーフ オープン ソケットを検出するのに長い時間がかかります。また、KEEPALIVE タイムアウト値はグローバル レジストリ値です:(

SO_RCVTIMEO setsockopt() オプションを使用して、recv() でより小さなソケットごとのタイムアウトを設定できます。このタイムアウトを使用して、ソケットをすぐに閉じるか、プロトコルで許可されている場合は、何らかのポーリング/エコー要求をピアに発行して、ピアがまだそこにあることを確認し、別のタイムアウトが発生した場合はソケットを閉じることができます。

于 2012-10-01T02:53:16.750 に答える