1

クライアントから、私が理解できないエラーが報告されました。TCPベースのクライアントは、データを受信するサーバーに接続されており、ほとんど何も送信しません。通常、すべてが正常に機能しますが、ブルームーンに入ると、次のような状況が発生します。

  • サーバーはいくつかのデータを送信します
  • クライアントがデータを受信する
  • クライアントがデータを処理しています
  • ...そしてその間にサーバーはより多くのデータを送信します
  • クライアントが処理を終了します
  • クライアントがソケットからデータを読み取ろうとします
  • クライアントは、処理後の最初のread()ステートメントで永久にハングします
  • サーバーが接続を閉じます
  • クライアントはまだハングしています

これがtcp接続が確立される方法です(すべてのログ、リターンチェックなどが削除されます)

ret = inet_pton(AF_INET, conn->address, &addr.sin_addr);
addr.sin_port        = htons(conn->port); /* Server port */
addr.sin_family      = AF_INET;
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
connect(sock, (struct sockaddr *) &addr, sizeof(addr));

そして、これが読み取りラッパーです。

int32_t _readn ( int fd, uint8_t *vptr, int32_t n )
{
  int32_t  nleft;
  int32_t  nread;
  uint8_t*     ptr;

  ptr = vptr;
  nleft = n;
  while (nleft > 0) {
    if ((nread = read (fd, ptr, nleft)) < 0) {
      if (errno == EINTR) {
        nread = 0;
      } else {
        return E_NETWORK_ERROR;
      }
    } else if ( nread == 0 ) {
      break;
    }
    nleft -= nread;
    ptr   += nread;
  }
  return  (n-nleft);
}

接続が閉じられた後でも、読み取り呼び出しが永久にブロックされる可能性はありますか?

これを引き起こす可能性があることに気づかなかった、ラッパーにある種のトリッキーなエラーがありますか?接続時にソケットにいくつかのフラグを設定する必要がありますか?

4

2 に答える 2

4

問題の原因は、読み取るデータがない場合、読み取りがブロックされることです。たとえば、予想される n バイトより少ない書き込みがある場合。これはブロッキング読み取りと呼ばれます。

データがあるかどうかを調べるには、selectJite の言うように使用します。

最後に、ファイアウォールがライブ接続を切断している可能性があります。一部のファイアウォールは、一定時間 (30 分など) を超えて開いている接続を切断するように構成されています。おそらくこれはあなたが持っているものではありません。

于 2013-02-27T14:28:55.487 に答える
2

データが利用可能かどうかを確認するために、選択ベースの関数を使用することになりました。

不可解なデータ損失の背後にある理由はまだ不明ですが (サーバーエラーは確認されていません)、これはうまくいくようです:

int32_t isReadDataAvailableOnSocket ( int sock, uint32_t waitTimeUs )
{
  fd_set fds;
  int16_t ret = 0;
  struct timeval timeout;
  struct timeval* timeoutPtr = NULL;

  if (waitTimeUs>0) {
    timeout.tv_sec = waitTimeUs / 1000000;
    timeout.tv_usec = waitTimeUs % 1000000;
    timeoutPtr = &timeout;
  }

  FD_ZERO ( &fds );
  FD_SET ( sock, &fds );

  ret = select ( sock+1, &fds, NULL, NULL, timeoutPtr );
  if (ret == -1) {
    WARN("select failed for udp socket=[%d]", sock);
    return E_NETWORK_ERROR;
  }
  if ( ! FD_ISSET(sock, &fds) )
  {
    return E_NO_DATA;
  }
  else
  {
    return 0;
  }
}
于 2013-03-19T14:21:21.230 に答える