混淆了boost :: asio :: yield在同一条链中的执行顺序

时间:2016-11-27 11:18:16

标签: c++ boost network-programming boost-asio

我正在尝试使用执行以下操作的asio编写客户端:

  1. 连接服务器。
  2. 尝试在连接到服务器之后读回一些数据
  3. 我发现的问题是操作似乎没有像我期望的那样按顺序执行。这是代码:

    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...
    

    即&#34;连接到服务器&#34;应始终之前等待从服务器套接字读取消息...&#34;。

    有谁知道发生了什么事?我以为strand_会保证执行顺序,但似乎我可能误解了一些东西。获得我想要的效果的正确解决方案是什么?

1 个答案:

答案 0 :(得分:1)

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

第一个任务在第二个任务之前启动,只会发生

为了保持顺序,我只创建了一个在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只是spawn +调用asyncReceiveMessageCoro的组合。