2016-11-27 1 views
1

で、私は以下のことを行いASIOとクライアントを記述しようとしていますについて混乱:ブースト:: ASIO ::降伏オーダー実行の同じ鎖

  1. がサーバーに接続します。
  2. のデータを読み取ろうとすると、がサーバーに接続されています。

私が見ている問題は、操作が順番に実行されていないように思われることです。ここでは、コードは次のとおりです。

std::future<NetMessage> Cliente::asyncConnectTo(std::string const & hostname, 
              int port, 
              std::string const & name) { 
    using namespace std::literals; 
    boost::asio::spawn 
     (strand_, 
     [this, name, port, hostname](boost::asio::yield_context yield) mutable { 
      i_->playerLog->trace() << name << " connecting to " << hostname << ':' 
            << port; 
      Guinyote::Utils::connectWith 
       (*this, std::move(hostname), port, 
       std::move(name), yield); 

      i_->playerLog->info() << "Connected to server."; 
     }); 
    runthread_ = std::thread([&] { 
      try { 
       i_->playerLog->info() << "Starting..."; 
       this->service_.run(); 
      } 
      catch (std::exception & e) { 
       std::cout << e.what() << std::endl; 
      } 
     }); 
    return this->asyncReceiveMessage(); //This function spawns another coroutine through the same strand_ object. 
} 

機能this->asyncReceiveMessage()は、サーバーが接続した後に送り返すメッセージ受け取ることが予想されます。

std::future<NetMessage> Cliente::asyncReceiveMessage() { 
    namespace ba = boost::asio; 

    std::promise<NetMessage> prom; 
    std::future<NetMessage> message = prom.get_future(); 
    ba::spawn 
     (strand_, 
     [this, p = std::move(prom)](boost::asio::yield_context yield) mutable { 
      i_->playerLog->trace("waiting to read message from server socket..."); 
      boost::system::error_code ec{}; 
      boost::int64_t messageSize{}; 
      ba::async_read(
       socket_, 
       ba::buffer(&messageSize, sizeof(boost::int64_t)), 
       yield); 

      i_->playerLog->trace() << "Client: Received message of " 
            << messageSize << " bytes. Reading message..."; 

      std::vector<char> serverMessageData(messageSize); 
      ba::async_read 
       (socket_, 
       ba::buffer(serverMessageData), 
       yield); 

      using namespace boost::iostreams; 
      basic_array_source<char> input_source(&serverMessageData[0], serverMessageData.size()); 
      stream<basic_array_source<char>> stream(input_source); 
      boost::archive::binary_iarchive archive(stream); 
      Utils::MensajeRed msg; 

      archive >> msg; 
      i_->playerLog->trace() << "NetMessage correctly read."; 
      p.set_value(std::move(msg)); 

     }); 
    return message; 
} 

私のログファイルでは、私はクライアント側で次のようになっていました:

[clientLog] [info] Client: Starting... 
[clientLog] [trace] User1234 connecting to localhost:10004 
[clientLog] [trace] waiting to read message from server socket... 

しかし、私はそのログの3行目は[clientLog] [info] Connected to server.後に来ることを期待するので、私の予想は以下の通りである:

[clientLog] [info] Client: Starting... 
[clientLog] [trace] User1234 connecting to localhost:10004 
[clientLog] [info] Connected to server. 
[clientLog] [trace] waiting to read message from server socket... 

すなわち、「サーバに接続された」常に「...サーバソケットからのメッセージを読むのを待っている」前が起こるはずです。

何が起こっているのか分かりませんか?私はstrand_が実行の順序を保証すると思ったが、何か誤解しているようだ。私が望む効果を得るための正しい解決策は何ですか?

+0

これは同じですが、私は適切な解決策を見ません。実際には「手で扱う」のようなものがあります:http://stackoverflow.com/questions/19946555/boostio-service-how-to-guarantee-handler-execution-sequence –

答えて

1

ここでの問題は、タスクが順番に開始されることですが、後でboost::asio::spawnがチェーン内にスポーン呼び出しを行っても、最初のタスクが2番目のタスクの前に終了するわけではありません。

最初のタスクは2番目のタスクの前に開始され、それ以外は何も起こりません。

順序を維持するために、2つの異なるコルーチンを生成する代わりに、asyncConnectToのスポーン内で呼び出されるコルーチンを作成しました。私は最初のコルーチンが第二の前に完了することを確認してください。この方法:

NetMessage Cliente::asyncReceiveMessageCoro(boost::asio::yield_context yield) { 
    namespace ba = boost::asio; 
    i_->playerLog->trace("waiting to read message from server socket..."); 
    boost::system::error_code ec{}; 
    boost::int64_t messageSize{}; 
    ba::async_read(
     socket_, 
     ba::buffer(&messageSize, sizeof(boost::int64_t)), 
     yield); 

    i_->playerLog->trace() << "Client: Received message of " 
            << messageSize << " bytes. Reading message..."; 

    std::vector<char> serverMessageData(messageSize); 
    ba::async_read 
     (socket_, 
     ba::buffer(serverMessageData), 
     yield); 

    using namespace boost::iostreams; 
    basic_array_source<char> input_source(&serverMessageData[0], serverMessageData.size()); 
    stream<basic_array_source<char>> stream(input_source); 
    boost::archive::binary_iarchive archive(stream); 
    Utils::MensajeRed msg; 

    archive >> msg; 
    i_->playerLog->trace() << "NetMessage correctly read."; 
    return msg; 
} 

このコルーチンは終わりに連鎖させることができます:

std::future<NetMessage> Cliente::asyncConnectTo(std::string const & hostname, 
              int port, 
              std::string const & name) { 
    using namespace std::literals; 

    std::promise<NetMessage> msgProm; 
    auto msg = msgProm.get_future(); 
    boost::asio::spawn 
     (strand_, 
     [this, name, port, hostname, p = std::move(msgProm)](boost::asio::yield_context yield) mutable { 
      i_->playerLog->trace() << name << " connecting to " << hostname << ':' 
            << port; 
      Guinyote::Utils::connectWith 
       (*this, std::move(hostname), port, 
       std::move(name), yield); 

      i_->playerLog->info() << "Connected to server."; 
      p.set_value(this->asyncReceiveCoro(yield)); 
     }); 
    runthread_ = std::thread([&] { 
      try { 
       i_->playerLog->info() << "Starting..."; 
       this->service_.run(); 
      } 
      catch (std::exception & e) { 
       std::cout << e.what() << std::endl; 
      } 
     }); 
    return msg; 
} 

私の古いasyncReceiveMessageはちょうどspawnasyncReceiveMessageCoroを呼び出し+の組み合わせになります。

+1

あなたの所見を共有していただきありがとう – sehe

関連する問題