3

ソケット(ブロッキング)で2バイトのアプリデータを10秒ごとに送信しますが、最後のインスタンスで送信呼び出しが40秒を超えて長くブロックされました。

  • 2012-06-13 12:02:46.653417|情報|送信前
  • 2012-06-13 12:02:46.653457 |情報|送信後(2)
  • 2012-06-13 12:02:57.566898|情報|送信前
  • 2012-06-13 12:02:57.566962 |情報|送信後(2)
  • 2012-06-13 12:03:08.234060|情報|送信前
  • 2012-06-13 12:03:08.234101 |情報|送信後(2)
  • ** 2012-06-13 12:03:19.010743|情報|送信前
  • 2012-06-13 12:04:00.969162 |情報|送信後(2)**

machine(linux)のtcpのデフォルトの送信バッファサイズは65536です。

2バイトのデータはサーバーとのハートビートであり、サーバーはクライアントが少なくとも15秒に1回HBを送信することを想定しています。

また、naggleのアルゴリズムを無効にしませんでした。

問題は、送信通話を40秒ほどブロックできるかどうかです。そして、それは散発的にしか起こっていません、それは12時間近く走った後に起こりました。

私が知っている送信呼び出しは、データをTCP送信バッファーにコピーするだけです。

公開は10秒ごとに呼び出されます。いいえ、呼び出しの送信が徐々に遅くなることはありません。突然発生し、反対側のソケットが閉じたため、アプリが終了します。

int publish(char* buff, int size) const {
      /* Adds the 0x0A to the end */
      buff[size]=_eolchar;

      if (_debugMode)
      {
          ACE_DEBUG((MY_INFO "before send\n"));
      }

      int ret = _socket.send((void*)buff, size+1);

      if (_debugMode)
      {
          ACE_DEBUG((MY_INFO "after send (%d)\n", ret));
          //std::cout << "after send " << ret << std::endl;
      }

      if (ret < 1)
      {
          ACE_DEBUG((MY_ERROR "Socket error, FH going down\n"));
          ACE_OS::sleep(1);
          abort();
      }
      return ret;
 }
4

2 に答える 2

3

ブロッキングsend()呼び出しを使用する場合、アプリケーションの観点からは、リモートTCPバッファー、ネットワーク、およびローカル送信TCPバッファーを1つの大きなバッファーと見なすことができます。

つまり、リモートアプリケーションがTCPバッファからの新しいバイトの読み取りを遅らせると、最終的にローカルTCPバッファが(ほぼ)いっぱいになります。send()TCPバッファーをオーバーフローする新しいペイロードを試行した場合、send()実装(カーネルシステムコール)は、TCPバッファーがそのペイロードを格納するのに十分なスペースを取得するまで、フォーカスをアプリケーションに戻しません。

その状態に到達する唯一の方法は、リモートアプリケーションが十分なバイトを読み取らない場合です。テスト環境での一般的なシナリオは、リモートアプリケーションがブレークポイントで一時停止する場合です... :-)

これは、SLOWCONSUMERの問題と呼ばれるものです。その診断を共有する場合、その問題を取り除くには複数の方法があります。

  1. リモートアプリケーションを制御できる場合は、ローカルアプリケーションがブロックされないように十分に高速にします。
  2. リモートアプリケーションを制御できない場合は、複数の答えがある可能性があります。
    • 最大40秒までブロックする必要がある場合でも問題ありません。
    • そうでない場合は、ブロック解除バージョンのsend()システムコールを使用する必要があります。ここから、複数の可能なポリシーがあります。以下に説明します。(少し待ってください! :-) )

偽の送信TCPFIFOとして機能し、送信呼び出しが返されると大きくなる動的配列の使用を試みることができますEWOULDBLOCK。この場合、select()システムコールを使用して、リモートアプリケーションがペースに追いついたことを検出し、最初に見えないデータを送信する必要があります。

publish()ここにある単純な関数(ほとんどのネットワークアプリケーションでは非常に一般的ですが)よりも少し注意が必要な場合があります。また、動的バッファが拡張されて空きメモリがなくなるという保証はなく、ローカルアプリケーションがクラッシュする可能性があることも知っておく必要があります。「リアルタイム」ネットワークアプリケーションの一般的なポリシーは、到達時にTCP接続を閉じるバッファの任意の最大サイズを選択することです。これにより、ローカルアプリケーションが空きメモリを使い果たすのを防ぎます。それは潜在的な遅い消費者接続の数に依存するので、賢明にそれを最大に選択してください。

于 2012-06-14T17:39:48.217 に答える
1

次の(およびこれからは触れない)システムコールのブロックと見なされます:
send、connect、recv、accept。

これが意味するのは、指定されたジョブが完了するまで、必要な範囲でブロックできるということです。そうです、データの送信にかかる時間に応じて、sendは40秒以上ブロックできます。あなたの特定のケースでなぜそれがそんなに長くブロックされたのか私にはわかりませんが。

このブロッキングを回避したい場合は、非同期ソケットとI/Oについて読むことをお勧めします。彼らはあなたの問題の一部を解決することを証明するかもしれません。

于 2012-06-14T17:20:12.777 に答える