c++ - 对同一链中的 boost::asio::yield 执行顺序感到困惑

标签 c++ boost network-programming 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_ 会保证执行顺序,但似乎我可能误解了一些东西。获得我想要的效果的正确解决方案是什么?

最佳答案

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

这只会发生第一个任务在第二个任务之前开始,没有别的。

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

关于c++ - 对同一链中的 boost::asio::yield 执行顺序感到困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40828311/

相关文章:

c++ - 关于 "Address-of operator"(&)和c、c++中数组的问题

python - do_handshake 有什么意义

iphone - CFWriteStreamWrite/CFReadStreamWriter 容易超时吗?

c++ - 使用 boost::swap 交换原始指针?

c++ - 解决 boost.python 中的重载

c++ - 如何获取所有子节点值

java - 使用异步任务将大字符串从 Android 发送到 Servlet

c++ - 卸载时XAudio2访问冲突异常

android - 使用 CMake 在 C++ android 应用程序中使用 libcurl

c++ - 您可以使用什么将 int/float 转换为 wchar_t*?