You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost.Asio Acceptor关闭重启异常:io_context.run()立即返回

问题:Boost Asio HTTP服务器重启后io_context::run()立即返回

问题背景

基于Boost官方单线程HTTP服务器示例修改,将地址解析、监听逻辑移至start_server(),停止逻辑移至end_server(),实现服务器启停功能。单次启停测试正常,但重启时调用io_context->run()会立即返回,服务器无法正常重启。

修改后的核心server_impl代码:

server_impl::server_impl(
    const std::string& address,
    const std::string& port,
    const std::shared_ptr<boost::asio::io_context>& io_context,
    const std::shared_ptr<connection_manager>& connection_manager,
    const std::shared_ptr<request_handler>& request_handler) :
        io_context_(io_context),
        acceptor_(*io_context_),
        connection_manager_(connection_manager),
        new_connection_(),
        request_handler_(request_handler),
        address_(address),
        port_(port)
{
}

void server_impl::start_server()
{
    boost::asio::ip::tcp::resolver resolver(*io_context_);
    boost::asio::ip::tcp::endpoint endpoint =
        *resolver.resolve(address_, port_).begin();
    acceptor_.open(endpoint.protocol());
    acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
    acceptor_.bind(endpoint);
    acceptor_.listen();
    start_accept();
}

void server_impl::end_server()
{
    acceptor_.close();
    connection_manager_->stop_all();
}

void server_impl::start_accept()
{
    new_connection_.reset(new connection_impl(
        io_context_,
        connection_manager_,
        request_handler_));
    acceptor_.async_accept(
        new_connection_->socket(),
        boost::bind(
            &server_impl::handle_accept,
            this,
            boost::asio::placeholders::error));
}

void server_impl::handle_accept(const boost::system::error_code& e)
{
    if (!acceptor_.is_open())
    {
        return;
    }

    if (!e)
    {
        connection_manager_->start(new_connection_);
    }

    start_accept();
}

void server_impl::serve_static(const std::string& path)
{
    request_handler_->serve_from_directory(path);
}

重启测试代码:

int main()
{
    server->serve_static("/path/to/html");
    server->start_server();

    std::thread t([&server](){sleep(30); server->end_server();});

    io_context->run();

    t.join();

    server->start_server();

    io_context->run(); // 此处立即返回,服务器未重启
}

原因分析

io_context::run()会在所有异步操作完成后退出,并将上下文置于停止状态。即使后续重新添加了新的异步操作(比如start_server()里的async_accept),直接调用run()时,因为上下文仍处于停止状态,会立即返回,不会处理新的异步任务。

解决方案

方案1:重启前调用io_context::restart()

在第二次调用io_context->run()之前,调用io_context->restart()重置上下文状态,清除停止标记,使其可以再次处理异步操作。

修改后的main函数:

int main()
{
    server->serve_static("/path/to/html");
    server->start_server();

    std::thread t([&server](){sleep(30); server->end_server();});

    io_context->run();

    t.join();

    // 重置io_context的停止状态
    io_context->restart();
    server->start_server();

    io_context->run();
}

方案2:使用work对象保持io_context活跃(可选)

如果希望io_context始终处于可运行状态,不会因无工作任务而退出,可以在初始化时创建work守护对象。这样run()会一直阻塞,直到显式取消work对象。

示例代码:

int main()
{
    // 创建work对象,保持io_context活跃
    auto work_guard = boost::asio::make_work_guard(*io_context);

    server->serve_static("/path/to/html");
    server->start_server();

    std::thread t([&server, &work_guard](){
        sleep(30); 
        server->end_server();
        // 取消work守护,让io_context可以退出
        work_guard.reset();
    });

    io_context->run();
    t.join();

    // 重新创建work对象
    work_guard = boost::asio::make_work_guard(*io_context);
    server->start_server();

    io_context->run();
}

额外注意事项

  • end_server()中调用acceptor_.close()会正确取消所有未完成的异步accept操作,重启时start_server()重新打开、绑定、监听acceptor的逻辑是正确的。
  • 确保connection_manager_->stop_all()能正确关闭所有活跃连接,避免残留异步任务干扰重启流程。

内容的提问来源于stack exchange,提问作者av4625

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 11:01:35