2011-02-07 10 views
17

クライアントとサーバーが非同期でboost::asioを使用しています。私は接続を閉じるためにいくつかのタイムアウトを追加し、何かがうまくいかない場合には再試行することがあります。boost :: asioの非同期呼び出しは自動的にタイムアウトしますか?

最初の考えでは、async_関数を呼び出すと、非同期操作が完了すると、deadline_timerが期限切れになるはずです。今私はそれがあらゆる場合に厳密に必要かどうか疑問に思います。例えば

  • async_resolveは、おそらく(例えばRES_TIMEOUTおそらく/etc/resolv.confで構成によってオーバーライドresolv.hで)、それに組み込まれたタイムアウトを有するシステムのリゾルバを使用します。自分のタイマーを追加することで、自分のリゾルバの働き方と矛盾することがあります。 async_connectについては

  • connect(2)システムコールは、それに組み込まれたタイムアウトのいくつかの並べ替え

  • を持っているなど

async_コールは "以内にハンドラを呼び出すことが保証されている(もしあれば)だから、合理的な "時間枠?そして、操作が[タイムアウトする]場合、ハンドラはbasic_errors::timed_outエラーまたは何か他のものを渡すでしょうか?

+0

私は自分自身の 'deadline_timer'を作成することにこだわらなければならなかったので、私はそれが不要であるかどうかを見ることに非常に興味があります。 – chrisaycock

+0

+1興味深い質問。答えは主にプラットフォームに依存すると思います。私はあなたがLinuxを気にしていると思いますか? –

答えて

29

私はいくつかのテストを行った。私の結果に基づいて、OSの実装に依存していることは明らかです。参考までに、私は株式Fedoraカーネルでこれをテストしました:2.6.35.10-74.fc14.x86_64

一番下の行は、async_resolve()あなたがdeadline_timerを設定せずに逃げることができるかもしれない唯一のケースになりそうだということです。合理的な行動をとるためには、他のすべてのケースで実際に必要です。


async_resolve()

async_resolve()への呼び出し4つのクエリ離れて5秒になりました。ハンドラは、要求の後、エラーboost::asio::error::host_not_foundで20秒後に呼び出されました。

リゾルバのデフォルト設定は5秒(2回の試行)(resolv.h)です。そのため、設定されているクエリ数の2倍が送信されているようです。この動作は、options timeoutoptions attempts/etc/resolv.confに設定することで変更可能です。いずれの場合も、送信されたクエリの数はattemptsに設定されたものの2倍であり、後でhost_not_foundエラーでハンドラが呼び出されました。

このテストでは、単一の構成済みネームサーバーがブラックホールルーティングされていました。ブラックホールルーティング先とasync_connect()を呼び出す


async_connect()

〜189秒後にエラーboost::asio::error::timed_outで呼び出されるハンドラになりました。

スタックは、最初のSYNと5回の再試行を送信しました。最初の再試行は3秒後に行われ、再試行タイムアウトは毎回倍増します(3 + 6 + 12 + 24 + 48 + 96 = 189)。リトライ回数を変更することができる。なければなりません

[再送タイマー] SYN セグメントについて:5のデフォルトは、RFC 1122(4.2.3.5)に適合するように選択される

% sysctl net.ipv4.tcp_syn_retries 
net.ipv4.tcp_syn_retries = 5 
少なくとも セグメントの再送信を提供するのに十分な大きさに設定してください。 少なくとも3分間。 アプリケーションでは、 の接続を閉じることができます(つまり、開かれた試行をあきらめて) 早く接続できます。

3分= 180秒ですが、RFCでは上限が指定されていません。実装が永遠に再試行するのを止めるものは何もありません。ソケットの送信バッファがフルではなかったよう

async_write()


は限り、このハンドラは、常にすぐに呼ばれていました。

私のテストでは、TCP接続が確立され、1分後にasync_write()というタイマーが設定されました。先にブラックホール後続のトラフィックへのダウンストリームルータの設定

  • :接続が確立したがasync_write()コールする前にされた分の間に、私は騒乱のすべての種類を試してみました。
  • ダウンストリームファイアウォールでセッションをクリアするので、宛先からの偽のRSTで応答します。
  • どんなに私が何をしたか/etc/init.d/network stop

を実行していない私のイーサネット

  • を抜く、次async_write()はすぐに成功を報告するために、そのハンドラを呼び出します。ファイアウォールがRSTを偽装された場合には

    、接続はすぐに閉じて、私は(すぐにboost::asio::error::connection_resetを報告する)オペレーションを実行しようとするまで、私がいることを知る方法がありませんでしたました。それ以外の場合、接続は開いたままになり、最終的に17〜18分後にタイムアウトするまでエラーを報告しません。

    async_write()の最悪の場合は、ホストが再送信中で、送信バッファがいっぱいである場合です。バッファがいっぱいになると、async_write()は再送がタイムアウトするまでハンドラを呼び出しません。 15の再送へのLinuxデフォルト:

    % sysctl net.ipv4.tcp_retries2 
    net.ipv4.tcp_retries2 = 15 
    

    は再送信の間の時間は、それぞれの後に増加する(そのような特定の接続の推定ラウンドトリップ時間などの多くの要因に基づいている)が、2分間でクランプされます。したがって、デフォルトの15回の再送信と最悪の場合の2分間のタイムアウトでは、async_write()ハンドラが呼び出される上限は30分です。呼び出されると、エラーはboost::asio::error::timed_outに設定されます。

    async_read()

    この


    は限り接続が確立されると、データが受信されないように、そのハンドラを呼び出すことはありません。私はそれをテストする時間がなかった。

  • +2

    これは答えになるはずです...私の短い答えよりもはるかに関連性の高い詳細を提供します。 – diverscuba23

    10

    これらの2回の呼び出しでは、ハンドラまでスケジュールされたタイムアウトが発生する可能性がありますが、いずれかのタイムアウトまでに時間がかかることがあります。 (私はちょうど接続を座って、プロセスを殺す前にboost::asioと10分以上の単一の接続呼び出しで接続しようとしていることを知っている)。また、async_readasync_writeコールにはタイムアウトが関連付けられていないため、読み取りと書き込みにタイムアウトが必要な場合は、まだdeadline_timerが必要です。

    +1

    +1が疑わしい場合は明示してください。動作が文書化されていない場合は、独自のタイマーを追加してください。 –

    +0

    これは私が見つけたものです。 'async_read'と' async_write' * do *は過度の再送が最終的に数十分後に接続を終了させるという意味でタイムアウトします。 – eater

    関連する問題