2016-11-27 70 views
1

的boost ::支持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... 

但我预计该日志的第三行会来[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

所以这里的问题是任务将按顺序启动,但后续的链接中产生的调用并不意味着第一个任务将在第二个任务完成之前完成。

刚刚发生的那个第一个任务是在第二个任务之前开始的,没有别的。

为了保持顺序,我只创建了一个在asyncConnectTo的spawn中调用的协程,而不是产生两个不同的协程。这样,我之前,请确保第二个第一协程完成:

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刚刚成为+调用asyncReceiveMessageCorospawn的组合。

+1

谢谢你花时间分享你的发现 – sehe