Boost.beast websocket - 如何为 io_context.run() 创建待处理的 async_read() queue/work?

Boost.beast websocket - How to make a pending async_read() queue/work for io_context.run()?

完全菜鸟在这里通过使用 Websocket 的 IoT 项目学习 c++。 至此,稍微成功修改了这个例子beast async_client_ssl与服务器握手。

我的问题是 ioc.run() 运行 在初始回调后停止工作并退出。 两年前我遇到了与 post 相同的问题。 Boost.best websocket ios.run() exits after sending a request and receiving one reply

上面链接 post 的答案非常简单(1. 和 2.),但我仍然不知道如何实现它。

1.在不阅读您的代码的情况下,了解 运行() 方法在没有未决工作时终止。例如,您的读取方法需要排队一个新的读取。

2。将 async_read() 移动到一个单独的函数,比方说 do_read(),并在 on_read() 的末尾以及当前位置调用它。

post里提问的人也显得很疑惑,不过这两个回答之后,就没有再解释了。那么,有没有人可以帮助我,或许可以提供一个简单的代码片段?

some other noob's previous post 代码中的 on_read() 中,我添加了 async_read() 如下所示。

void on_read(boost::system::error_code ec, std::size_t bytes_transferred)
{
     io_in_pr34ogress_ = false; // end of write/read sequence
     boost::ignore_unused(bytes_transferred);

     if(ec)
        return fail(ec, "read");
     else
        std::cout << "on_read callback : " << boost::beast::buffers(buffer_.data()) << std::endl;
           
     // Clear the Buffer
     //~ buffer_ = {};
     buffer_.consume(buffer_.size());
     ws_.async_read(buffer_, std::bind(&session::on_read, shared_from_this(), 
                    std::placeholders::_1, std::placeholders::_2));
}

但没有希望。 ioc.run 刚刚结束。那么如何正确地做上述 1. 和 2. 的回答呢?

谢谢!

----------------更新于 2021 年 10 月 25 日---------------- ---

@sehe 的回答与执行者合作。 为此,我不得不将 boost 版本从 1.67 升级到 1.7 以上(我使用的是 1.74)。这解决了我的问题,但如果有人为那里的人们提供了适用于 1.67 的有效解决方案,请分享这个想法:)

好吧,最简单的就是加一个work_guard。更合乎逻辑的做法是将 thread_pool 作为执行上下文。

打一个工作警卫:

boost::asio::io_context ioc;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type>
    work = make_work_guard(ioc.get_executor());

(或简称 auto work = make_work_guard(...);.

如果在某个时候你想要 运行 return,释放工作守卫:

work.reset();

线程池

前一种略过了“明显”的事实,即您需要另一个线程来 run() 服务 reset()工作守卫。

相反,我建议首先将线程的 运行 留给 Asio:

boost::asio::thread_pool ioc(1);

这和 io_context 一样是一个 执行上下文 :

int main()
{
    // +-----------------------------+
    // | Get azure access token      |
    // +-----------------------------+
    static std::string const accessToken =
        "wss://sehe797979.webpubsub.azure.com/client/hubs/"
        "Hub?access_token=*************************************"
        "**********************************************************************"
        "**********************************************************************"
        "**********************************************************************"
        "************************************************************";
    // get_access_token();

    // +--------------------------+
    // | Websocket payload struct |
    // +--------------------------+
    struct Payload payload = {0, "", "text", "test", "joinGroup"};

    // +---------------------------------+
    // | Websocket connection parameters |
    // +---------------------------------+
    std::string protocol = "wss://";
    std::string host     = "sehe797979.webpubsub.azure.com";
    std::string port     = "443";
    std::string text     = json_payload(payload);

    auto endpointSubstringIndex = protocol.length() + host.length();

    // Endpoint
    std::string endpoint = accessToken.substr(endpointSubstringIndex);
    //std::cout << "Endpoint : " << endpoint << std::endl;
    
    // The io_context is required for all I/O
    boost::asio::thread_pool ioc(1);

    // The SSL context is required, and holds certificates
    ssl::context ctx{ssl::context::sslv23_client};

    // This holds the root certificate used for verification
    load_root_certificates(ctx);

    // Launch the asynchronous operation
    std::shared_ptr<session> ws_session =
        std::make_shared<session>(ioc.get_executor(), ctx);
    ws_session->open(host, port, endpoint);

    // Run the I/O service. The call will return when the socket is closed.
    // Change the payload type
    payload.type = "sendToGroup";

    // +--------------+
    // | Send Message |
    // +--------------+
    // Get the input and update the payload data
    while (getline(std::cin, payload.data)) {
        // Send the data over WSS
        ws_session->write(json_payload(payload));
    }

    ioc.join();
}

这需要对 session 构造函数进行最少的更改以采用执行程序而不是 io_context&:

template <typename Executor>
explicit session(Executor executor, ssl::context& ctx)
    : resolver_(executor)
    , ws_(executor, ctx)
{
}

这是一个完全独立的编译演示 Live On Coliru