2017-05-09 3 views
1

サーバーから可変長メッセージを受け取る必要があるクライアント(Windows 10、Visual C++)にboost :: asioを使用しています。 メッセージは非常に頻繁に(毎秒10メッセージ以上)、各メッセージは約40-100バイトです。私はこのようにasync_read_somestreambufを使用していWindowsでのboost :: asioでの最適バッファーサイズ

void Client::readStart(void) 
{ 
    boost::asio::streambuf::mutable_buffers_type buf = _inbox.prepare(std::max((size_t)1024, _socket->available())); 

    // Start an asynchronous read and call readHandler when it completes or fails 
    _socket->async_read_some(buf, 
     boost::bind(&Client::readHandler, 
     this, 
     boost::asio::placeholders::error, 
     boost::asio::placeholders::bytes_transferred)); 
} 

つまり、私は、クライアントがまだあるため、多くのメッセージが蓄積してきたときに大きなバッファを使用する_inbox.prepare(std::max((size_t)1024, _socket->available()))で動的にバッファサイズを調整しようとしています以前のメッセージを処理します。

_inbox.prepare(262144)のような大きなバッファを使用することはできません。readHandlerは、より頻繁ではなく、大量のデータで呼び出されるためです。

動的バッファ割り当てを試みても、私は奇妙な遅延とデータの蓄積を経験します。

この

は私のログです:

2017-05-09 09:02:25 <debug> Received 1024 bytes 
    2017-05-09 09:02:25 <debug> Received 372 bytes 
    2017-05-09 09:02:25 <debug> Received 844 bytes 
    2017-05-09 09:02:25 <debug> Received 169 bytes 
    2017-05-09 09:02:25 <debug> Received 1024 bytes 
    2017-05-09 09:02:25 <debug> Received 379 bytes 
    2017-05-09 09:02:25 <debug> Received 1385 bytes 
    2017-05-09 09:02:25 <debug> Received 1421 bytes 
    2017-05-09 09:02:25 <debug> Received 108 bytes 
    2017-05-09 09:02:25 <debug> Received 1024 bytes 
    2017-05-09 09:02:25 <debug> Received 1768 bytes 
    2017-05-09 09:02:27 <debug> Received 65536 bytes 
    2017-05-09 09:02:33 <debug> Received 65536 bytes 
    2017-05-09 09:02:40 <debug> Received 65536 bytes 
    2017-05-09 09:02:47 <debug> Received 65536 bytes 
    2017-05-09 09:02:55 <debug> Received 65536 bytes 
    2017-05-09 09:03:01 <debug> Received 65536 bytes 
    2017-05-09 09:03:07 <debug> Received 65536 bytes 
    2017-05-09 09:03:15 <debug> Received 65536 bytes 
    2017-05-09 09:03:35 <debug> Received 65536 bytes 
    2017-05-09 09:03:41 <debug> Received 65536 bytes 
    2017-05-09 09:03:46 <debug> Received 65536 bytes 
    2017-05-09 09:03:50 <debug> Received 65536 bytes 
    2017-05-09 09:03:58 <debug> Received 65536 bytes 
    2017-05-09 09:04:02 <debug> Received 65536 bytes 
    2017-05-09 09:04:11 <info> Disconnected by remote host 

あなたが見ることができるように午前9時02分25秒すべてがOKになるまで、そのデータを蓄積するために開始しreadHandlerが頻繁に呼び出されます(各呼び出しの間に7-8秒)大きなデータ(65536バイト)のチャンクがあります。

最後に、リモートホストは接続を切断します。この切断は、サーバーからクライアント(Wiresharkでトレース)に送信されたTCP ZeroWindowプローブによるものです。つまり、TCPバッファがいっぱいです。

readHandlerが非常に頻繁に呼び出されるのはなぜですか。(クライアントの側では100%CPUの問題ではないと確信しています。クライアントはメッセージとCPUを処理するのが速いです負荷が小さい)

EDIT:

私はこのコードのソケットにNagleアルゴリズムを無効にしています:

boost::system::error_code error; 
_socket->set_option(tcp::no_delay(true), error); 

パケットをグループ化するからTCP/IPスタックを防止するための試みで、それはdoesnの助けてください。

EDIT 2:

ボトルネックがどこかに私の処理コードでがあるようですので、私は実際にすぐに十分なデータを受信して​​いないことだし、サーバのネーグルALGOは、以下のR. Joinyで説明した問題を生成。

+0

Wiresharkで送信されたすべてのイーサネットパッケージを識別できますか?おそらく、送信側のネットワークバッファがそれらを1つのイーサネットパッケージにまとめると決めているので、読み込みハンドラはあまり頻繁に呼び出されるのではなく、より多くのデータで呼び出されます。私は確信していませんが、これは通常非常に高い送信レートでしか発生しませんが、これをチェックするには傷つきません –

+0

パスMTU、イーサネットなどのために、1500バイトを超える受信TCPセグメントを決して得られません。 – EJP

+0

@ R.Joiny 65536バイトは単一のイーサネットパケットに入れることはできませんが、EJPは正しいです。他にも問題があるはずです。いずれにしても、Wiresharkトレースをより詳細に分析し、関連するものがあれば質問を更新しようとします。 –

答えて

1

私は100%ではありませんが、99%は確信していますが、私は答えを決めたので、長すぎるコメントを書いていました。

@MSaltersには一種のポイントがあります(ジャンボフレームでさえも64Kよりはるかに小さいですが)。 TCPパッケージは正確に最大64Kのサイズになります。これは明らかにログに表示されます。 Socketがすべてのtcpパッケージを最大64Kサイズのものにパックすることを決めた場合、もちろん複数のイーサネットパッケージ経由で送信されますが、受信ソケットは1TCPパッケージを完了しますので、tcpパッケージサイズに影響を与えないイーサネットMTUもあります最後のイーサネットパッケージを受信します。

これはコメントです。私が言いたいのは次のとおりです:

あなたのサーバーはデータを急速に送信する=サーバープログラムはソケットバッファーにすばやく書き込みます。

  • Socketは、タイマーを使用してデータをさらに待つことにします。タイマーがアクティブな間にデータが到着した場合は、データを送信TCPパッケージに追加します。非常に速い速度で送信するので、これはほとんどの場合です。そのため、tcpパッケージは最大サイズ64Kに達します。
  • ソケットはパッケージを送信するので、OSはそれをMTUサイズの一部に分割します。

彼らはMTUに収まるように、オペレーティング・システムは、ネットワークアダプタにMTUよりも大きなパケットを通過している、そしてネットワークアダプタドライバは、それらを破壊されます。 (出典:Wireshark forums

  • 受信ソケットは、これらすべてのイーサネットパッケージを取得しますが、最後のイーサネットパッケージが受信されるまで、すべてが1つのTCPパッケージと待機していることを見ています。
  • すべての小さなイーサネットパッケージのtcpパッケージを作成し、それを受信バッファに書き込みます。...
  • ...あなたのasync_readハンドラを目覚めさせます。

これはinteresting for youです。問題の


ソリューション:

  • あなたはサーバーのコードへのアクセス権を持っている場合は、そのソケットを使用して編集(ネーグル)を行います。

  • end-flagバイトなどでプロトコルの種類を定義する必要がない場合は、単一の小さなパッケージがどこで終了したかを知ることができます。 (サーバーにまだアクセスする必要があります:D)

  • 接続のシャットダウンのエラーは、クライアントがバッファーを十分に速く空にしないという問題です。しかし、これはどちらの方法でも起こります。なぜなら、送信されたデータxは時間の経過とともに常に同じであるからです。 (10倍〜100秒あたりのバイト数または1時間10秒あたり10000のバイトが同じである)


EDIT:

私はSTHを使用することをお勧めします。 tcp_clientスレッドにデータを書き戻してメインスレッドにポップしてデータを計算するスレッドセーフ循環バッファーのようなものです。この構造で、私は一度、1ミリ秒で私に送られたCSVに500バイトのデータを受信して​​保存することができました。私はArchLinuxのBeagleBoneBlackと私の(boost/asioが実現した)Applicationでtcpサーバをホストしています。

+0

あなたの答えをありがとう、それはあなたが記述した通りであるように思えます。残念ながら私はサーバーコードにアクセスすることはできませんが、私のコードでも問題が見つかりました。データを処理するのに十分速いと思ったのですが、代わりに自分のコードのどこかにボトルネックがなければなりませんサーバーのNagleアルゴリズムがすべてのパケットをいっぱいにしている理由:パケットをすばやく確認できないためです。サーバーのコードにアクセスできない場合でも、自分のボトルネックを取り除くと、サーバーがパケットをグループ化できなくなる可能性があります。 –

+1

はい、TCP Ackメッセージのサーバーが小さすぎるバッファー彼はグループ化を開始する。 –

+1

私は一度やった自分のプロジェクトの経験から私の答えを編集しました。たぶん役立つかもしれません:) –

関連する問題