2017-08-28 8 views
1

私はBoost.Asioを広範囲に使用しましたが、私が理解していない単体テストの問題に遭遇しました。私はコンパイルBoost.Asioはわかりにくい例をブロックしました

#include <string> 
#include <chrono> 
#include <thread> 
#include <mutex> 
#include <condition_variable> 

#include <boost/asio.hpp> 
#define BOOST_TEST_MODULE My_Module 
#define BOOST_TEST_DYN_LINK 
#include <boost/test/unit_test.hpp> 
#include <boost/test/auto_unit_test.hpp> 

using namespace std::string_literals; 
using namespace std::chrono_literals; 

namespace BA = boost::asio; 
namespace BAI = BA::ip; 

BOOST_AUTO_TEST_CASE(test) 
{ 
    std::mutex m; 
    std::condition_variable cv; 

    BA::io_service servicer; 
    auto io_work = std::make_unique<BA::io_service::work>(servicer); 

    auto thread = std::thread{[&]() { 
     servicer.run(); 
    }}; 

    auto received_response = false; 

    auto server_buf = std::array<std::uint8_t, 4096>{}; 
    auto server_sock = BAI::tcp::socket{servicer}; 
    auto acceptor = BAI::tcp::acceptor{servicer, 
             BAI::tcp::endpoint{BAI::tcp::v4(), 20123}}; 
    acceptor.async_accept(server_sock, [&](auto&& ec) { 
     if (ec) { 
      BOOST_TEST_MESSAGE(ec.message()); 
     } 
     BOOST_REQUIRE(!ec); 

     BOOST_TEST_MESSAGE("Accepted connection from " << server_sock.remote_endpoint() << 
          ", reading..."); 

     BA::async_read(server_sock, 
         BA::buffer(server_buf), 
         [&](auto&& ec, auto&& bytes_read){ 
      std::unique_lock<decltype(m)> ul(m); 
      received_response = true; 

      if (ec) { 
       BOOST_TEST_MESSAGE(ec.message()); 
      } 
      BOOST_REQUIRE(!ec); 

      const auto str = std::string{server_buf.begin(), 
             server_buf.begin() + bytes_read}; 
      BOOST_TEST_MESSAGE("Read: " << str); 

      ul.unlock(); 
      cv.notify_one(); 
     }); 
    }); 

    const auto send_str = "hello"s; 
    auto client_sock = BAI::tcp::socket{servicer, BAI::tcp::v4()}; 
    client_sock.async_connect(BAI::tcp::endpoint{BAI::tcp::v4(), 20123}, 
           [&](auto&& ec) { 
     if (ec) { 
      BOOST_TEST_MESSAGE(ec.message()); 
     } 
     BOOST_REQUIRE(!ec); 

     BOOST_TEST_MESSAGE("Connected..."); 
     BA::async_write(client_sock, 
         BA::buffer(send_str), 
         [&](auto&& ec, auto&& bytes_written) { 
      if (ec) { 
       BOOST_TEST_MESSAGE(ec.message()); 
      } 
      BOOST_REQUIRE(!ec); 

      BOOST_TEST_MESSAGE("Written " << bytes_written << " bytes"); 
     }); 
    }); 

    std::unique_lock<decltype(m)> ul(m); 
    cv.wait_for(ul, 2s, [&](){ return received_response; }); 
    BOOST_CHECK(received_response); 

    io_work.reset(); 
    servicer.stop(); 
    if (thread.joinable()) { 
     thread.join(); 
    } 
} 

g++ -std=c++17 source.cc -l boost_unit_test_framework -pthread -l boost_system -ggdb 

出力は次のようになります。

Accepted connection from 127.0.0.1:51688, reading... 
Connected... 
Written 5 bytes 

をし、それがタイムアウト私がダウンして非常に不自然な例に問題を低減しました。

デバッガを実行すると、async_readハンドラが呼び出されないことが示されます。何も実行されていないフェーズで実行を一時停止すると、メインスレッドがcondition_variablecv)で待機し、io_serviceスレッドがepoll_waitに待機していることが示されます。

私はデッドロックしているようですが、どのように見えません。

+0

最初の推測:クライアントがシャットダウンを発行していないため、送信されたデータ(4096)(send_string.size())を読み込もうとしているため、読み込みが完了していません。 –

+0

@RichardHodgesしかし、私は応答が必要な場合、シャットダウンを発行すると、接続を受け取る前に接続が切断されますか?私は、送信者のバッファがOSのMTUの下にあるため、受信したパケットの後にサーバーソケットが読み取りハンドラを起動するため、私はあまりにも多くのことを想定していますか? – cmannett85

+0

@RichardHodges書き込みハンドラの一番下に 'client_sock.shutdown(..)'を追加しようとしましたが、読み込みハンドラで 'End of stream'エラーが発生したばかりです。読取りバッファを5バイトに設定するとシステムが動作するようになりました。原則として間違いありませんが、あらかじめ送信サイズを知ることはできません。 – cmannett85

答えて

4

これは機能が動作するように定義されています。バッファが(http://www.boost.org/doc/libs/1_62_0/doc/html/boost_asio/reference/async_read/overload1.html)のスペースを持つバイト数を正確に待機します。

は代わりにこの方法を試してください。http://www.boost.org/doc/libs/1_62_0/doc/html/boost_asio/reference/async_read/overload2.html

をお読みが完了したかどうかを決定するためにコールバックを与えることができ、それは作家がそのメッセージを書いていたら、あなたの場合(待っていると、別のチャネルが提供する長さをチェック含めることができますそれを行うデッドロックフリーの方法を決めた)か、または適切なメッセージの直前。この終了条件を追加する

はそれを仕事になります:

[&](auto&& ec, auto&& bytes_read){ 
    return bytes_read < 5 ? 5 - bytes_read : 0; 
}, 
1

@codeshotが提供する答えが正しいですが、それはいくつかの解決策の一つである - 最も適切である完全にあなたが使用しているプロトコルに依存していますTCP接続を介して

  1. を固定長ヘッダ
  2. を得るために、固定長バッファに読み取ること boost::asio::async_read(または同等)を使用:例えば

    は、伝統的なキーの長さ - 値スタイルのプロトコルでは、2つの読み取りを行うことになります

  3. 使用し、必要なサイズのバッファを作成し、それは

chat server example codeにおけるこの良い例があります使用して、ステップ1を繰り返すヘッダで指定された長さ。

HTTPまたはRTSPを使用していた場合(後者は私がやろうとしていたことです)、データがどれだけ届いているのか分かりません。これは、応答、チャンク転送符号化などのヘッダがContent-Lengthであるために単純化されていますが、私には負担です)。このためにはasync_read_some(または同等のもの)が必要です(HTTP server exampleを参照)。

関連する問題