2017-08-15 17 views
4

デバイスからデータを受け取り、処理するサーバーを作成しています。ネットワークに中断がない限り(つまり、イーサネットケーブルを抜いてから再接続しない限り)、すべて正常に動作します。私はread_until()を使用しています。これは、デバイスが使用するプロトコルが特定のバイトシーケンスでパケットを終了させるためです。データストリームが中断されると、read_until()が期待通りにブロックされます。しかし、ストリームが再び起動すると、ストリームはブロックされたままです。私がWiresharkでデータストリームを見ると、デバイスは送信を続け、各パケットはネットワークスタックによってACKされます。しかし、bytes_readableを見ると、常に0になります。どのようにして割り込みを検出し、データストリームへの接続を再確立することができますか?以下は、コードスニペットであり、事前にお手伝いいただけるおかげです。 [私に簡単に行く、これが私の最初のスタックオーバーフローの質問です....はい、私は答えを検索してみました。]boost :: asioを使用してネットワーク中断から復旧する方法

using boost::asio::ip::tcp; 

boost::asio::io_service IOservice; 
tcp::acceptor acceptor(IOservice, tcp::endpoint(tcp::v4(), listenPort)); 
tcp::socket socket(IOservice); 
acceptor.accept(socket); 

for (;;) 
{ 
    len = boost::asio::read_until(socket, sbuf, end); 
    // Process sbuf 
    // etc. 
} 

答えて

2

覚えておいて、クライアントが接続を開始するので、あなたが必要とする唯一のもの達成するためには、ソケットを再作成し、再び受け入れを開始することです。あなたのスニペットのフォーマットを維持しますが、実際のコードが適切にカプセル化されていることを願っています。

using SocketType = boost::asio::ip::tcp::socket; 

std::unique_ptr<SocketType> CreateSocketAndAccept(
    boost::asio::io_service& io_service, 
    boost::asio::ip::tcp::acceptor& acceptor) { 
    auto socket = std::make_unique<boost::asio::ip::tcp::socket>(io_service); 
    boost::system::error_code ec; 
    acceptor.accept(*socket.get(), ec); 
    if (ec) { 
    //TODO: Add handler. 
    } 
    return socket; 
} 

... 
auto socket = CreateSocketAndAccept(IOservice, acceptor); 
for (;;) { 
    boost::system::error_code ec; 
    auto len = boost::asio::read_until(*socket.get(), sbuf, end, ec); 
    if (ec) // you could be more picky here of course, 
      // e.g. check against connection_reset, connection_aborted 
    socket = CreateSocketAndAccept(IOservice, acceptor); 
    ... 
} 

脚注:socketは範囲にとどまる必要があります。

編集:以下のコメントに基づいています。

リスニングソケット自体は、クライアントがサイレントであるか、切断されたかどうかを認識しません。すべての操作、特に同期は、完了時に時間制限を課す必要があります。 SO_RCVTIMEOまたはSO_KEEPALIVE(ソケットまたはシステム全体の詳細はHow to use SO_KEEPALIVE option properly to detect that the client at the other end is down?)を設定することを検討してください。

もう一つのオプションは、非同期に行き、完全な本格的な "共有"ソケットサーバーを実装することです(BOOSTの例のページは素晴らしいスタートです)。

いずれにしても、データの一貫性の問題に遭遇し、それに対処する必要があります。クライアントが中断した接続を検出すると、データを再送信します。 (または、より高度なプロトコルを使用してもっと複雑なもの)

+0

トムに感謝します。しかし、問題は、私がプラグを引いたときにread_until()がエラーコードを返さないということです。それは単にブロックし、決して戻ることはありません。 – Schnizz

+1

これについて考えると、意味があると言いたいと思います。サーバー(受信ソケット)には、ワイヤの非アクティブと非正常的なシャットダウン接続を区別する方法がありません。ソケットで 'SO_RCVTIMEO'を設定するか、デッドラインタイマーの周りに非同期ラッパーを実装して、非同期読み取りを実行するio_serviceを停止することを検討します。いずれにしても、タイムアウトに関する外部情報が必要だと言います。つまり、受け入れられたTCP接続用に複数の「共有」ソケットを作成できるという完全な非同期化をお勧めします。 –

+0

ええ、私は完全な非同期が問題を解決するかどうか疑問に思っていた。私はそれを与えるだろう。ヘルプをよろしくお願いいたします。 – Schnizz

1

同期をとどめたい場合は、中断を検出したときにソケットを破壊することがあります。ブロッキングコールは、キャッチして接続を再度受け入れることができる例外をスローする必要があります。トムは彼の答えに言及として、あなたがこれを検出するために、外部のメカニズムを必要とするので、

for (;;) 
{ 
    try { 
     len = boost::asio::read_until(socket, sbuf, end); 
     // Process sbuf 
     // etc. 
    } 
    catch (const boost::system::system_error& e) { 
    // clean up. Start accepting new connections. 
    } 
} 

は、非アクティブと無様な切断の間に違いはありません。

継続的なデータ転送が必要な場合は、サーバー側の接続ごとにタイムアウトが必要な場合があります。シンプルなpingも機能します。接続を受け入れると、X秒ごとにクライアントにpingを送信し、応答がない場合は接続が死んでいると宣言します。

+0

例外は 'boost :: system :: error_code'を使うのと同じことに言及する価値があります。実際は、前者は通常のシステムエラーの上に実装されます(スローされます)。 –

+0

私はTomが提案したようにタイムアウトを実装しましたが、Windowsでは動作しますが、Linuxでは動作しません。長期的には、asyncはおそらくより優雅な選択肢でしょう。したがって、ある時点で、非同期メソッドを使用するようにリファクタリングします。お二人のお手伝いをありがとう。 – Schnizz

関連する問題