2016-11-27 71 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