4

私はTCP接続を介してデータをエクスポートするサーバーを担当しています。サーバーが送信するデータレコードごとに、クライアントは短い「\n」確認メッセージを送り返す必要があります。送信した確認応答がWebサーバーから読み取られないと主張する顧客がいます。以下は、ソケットのI/Oに使用しているコードです。

bool can_send = true;
char tx_buff[1024];
char rx_buff[1024];
struct pollfd poll_descriptor;
int rcd;

poll_descriptor.fd = socket_handle;
poll_descriptor.events = POLLIN | POLLOUT;
poll_descriptor.revents = 0;
while(!should_quit && is_connected)
{
   // if we know that data can be written, we need to do this before we poll the OS for
   // events.  This will prevent the 100 msec latency that would otherwise occur
   fill_write_buffer(write_buffer);
   while(can_send && !should_quit && !write_buffer.empty())
   {
      uint4 tx_len = write_buffer.copy(tx_buff, sizeof(tx_buff));
      rcd = ::send(
         socket_handle,
         tx_buff,
         tx_len,
         0);
      if(rcd == -1 && errno != EINTR)
         throw SocketException("socket write failure");
      write_buffer.pop(rcd);
      if(rcd > 0)
         on_low_level_write(tx_buff, rcd);
      if(rcd < tx_len)
         can_send = false;
   }

   // we will use poll for up to 100 msec to determine whether the socket can be read or
   // written
   if(!can_send)
      poll_descriptor.events = POLLIN | POLLOUT;
   else
      poll_descriptor.events = POLLIN;
   poll(&poll_descriptor, 1, 100);

   // check to see if an error has occurred
   if((poll_descriptor.revents & POLLERR) != 0 ||
      (poll_descriptor.revents & POLLHUP) != 0 ||
      (poll_descriptor.revents & POLLNVAL) != 0)
      throw SocketException("socket hung up or socket error");

   // check to see if anything can be written
   if((poll_descriptor.revents & POLLOUT) != 0)
      can_send = true;

   // check to see if anything can be read
   if((poll_descriptor.revents & POLLIN) != 0)
   {
      ssize_t bytes_read;
      ssize_t total_bytes_read = 0;
      int bytes_remaining = 0;
      do
      {
         bytes_read = ::recv(
            socket_handle,
            rx_buff,
            sizeof(rx_buff),
            0);
         if(bytes_read > 0)
         {
            total_bytes_read += bytes_read;
            on_low_level_read(rx_buff,bytes_read);
         }
         else if(bytes_read == -1)
            throw SocketException("read failure");
         ioctl(
            socket_handle,
            FIONREAD,
            &bytes_remaining);
      }
      while(bytes_remaining != 0);

      // recv() will return 0 if the socket has been closed
      if(total_bytes_read > 0)
         read_event::cpost(this);
      else
      {
         is_connected = false;
         closed_event::cpost(this);
      }
   }
}

このコードは、poll()がレベルトリガー関数であり、ソケットから読み取るデータがある限りすぐにブロックを解除するという前提に基づいて作成しました。私が読んだことはすべて、この仮定を裏付けているようです。上記のコードが読み取りイベントを見逃す原因となる、見逃した可能性がある理由はありますか?

4

3 に答える 3

5

エッジトリガーではありません。常にレベルトリガーされます。ただし、実際の質問に答えるには、コードを読む必要があります。しかし、それはタイトルの質問に答えます。:-)

私はあなたのコードにあなたが見ている振る舞いを見ているかもしれない理由をはっきりと見ることができません。しかし、あなたの質問の範囲はあなたが提示しているコードよりもはるかに大きく、これが完全な問題診断であるとは言えません。

于 2013-02-25T17:08:10.617 に答える
2

レベルトリガーされます。ポーリング時にソケット受信バッファーにデータがある場合はPOLLINが起動し、ソケット送信バッファー(ほとんどの場合は存在します)に空きがある場合はPOLLOUTが起動します。

于 2013-02-26T01:02:24.827 に答える
1

問題の独自の評価に基づいて(つまりpoll、確認応答を読み取ることができると期待するときにブロックされます)、最終的にタイムアウトになります。

顧客のマシンがサーバーから50ミリ秒以上離れている場合は、100ミリ秒しか待機しないため、確認応答を受信する前に常に接続でタイムアウトになります。これは、データが顧客に届くまでに最低50ミリ秒かかり、確認応答が返されるまでに最低50ミリ秒かかるためです。

于 2013-02-25T19:18:59.230 に答える