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

如何正确处理boost::asio::io_context状态以持续接受客户端请求

问题

使用boost::asio::io_context和async_accept处理大量客户端请求时,用10个Python telnet脚本持续发送请求,运行两小时后服务端不再接受新连接。排查发现io_context.stopped()从未触发,"svc is end"日志也未打印,需要解决服务端长时间运行下持续接受连接的问题。

Python测试脚本

host = "192.168.16.178"
port = 12345

while True:
    tn = telnetlib.Telnet()
    tn.open(host, port)
    
    command = "show interfaces"
    
    tn.write(command.encode('ascii')+b'\n')
    
    
    tn.close()
    
    time.sleep(3);

服务端C++核心代码

int ClientManager::Init()
{
    if (this->_listenPort != 0)
    {
        desinit(0);

        _clientAcceptor = new ip::tcp::acceptor(_ioContext, ip::tcp::endpoint(ip::tcp::v6(), _listenPort));

        boost::asio::ip::tcp::acceptor::reuse_address option(true);
        _clientAcceptor->set_option(option);
        _clientAcceptor->listen();

        AsyncAccept();
    }

    _threadSvc = std::thread(&ClientManager::Svc, this);

    return 0;
}


void ClientManager::AcceptHandler(const boost::system::error_code ec, boost::asio::ip::tcp::socket socket)
{   
    if (ec)
    {
        console->error(PACK("Error code {}, msg:{}"), ec.value(), ec.message());
        Loge( "Error code {}, msg:{}", ec.value(), ec.message());

        return;
    }

    auto client = std::make_shared<ClientBridge>(std::move(socket));
    client->GetConnection();    

    Logi( "Got Connection from :{},port:{},use cnt:{}", client->PeerName(), client->PeerPort(), client.use_count());

    //继续监听
    AsyncAccept();
}


void ClientManager::AsyncAccept()
{
    if (_ioContext.stopped())
    {
        _ioContext.restart();
        Loge("_ioContext.restart1");
        _ioContext.run();
        Loge("_ioContext.restart");
    }

    _clientAcceptor->async_accept([this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) { AcceptHandler(ec, std::move(socket)); });
}


void ClientManager::Svc()
{
    Logi( "svc is being...");

    _ioContext.run();

    
    Logi( "svc is end");
}
解决方案

1. 修正AsyncAccept的错误逻辑

当前AsyncAccept中在io_context停止时调用run()会阻塞当前线程,导致后续async_accept无法执行,而且io_context的运行应该由专门的Svc线程负责,不能在其他线程中随意调用run()。直接移除多余的重启和run()调用:

void ClientManager::AsyncAccept()
{
    // 无需在此处理io_context状态,Svc线程已负责run()循环
    _clientAcceptor->async_accept(
        [this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) {
            AcceptHandler(ec, std::move(socket));
        });
}

2. 错误发生后仍保持监听

当前AcceptHandler在出现错误时直接返回,没有重新启动异步接受,一旦发生错误服务端就会停止监听新连接。修改错误分支,确保无论是否出错都继续监听:

void ClientManager::AcceptHandler(const boost::system::error_code ec, boost::asio::ip::tcp::socket socket)
{   
    if (ec)
    {
        console->error(PACK("Error code {}, msg:{}"), ec.value(), ec.message());
        Loge( "Error code {}, msg:{}", ec.value(), ec.message());
        // 错误后仍需重启监听
        AsyncAccept();
        return;
    }

    auto client = std::make_shared<ClientBridge>(std::move(socket));
    client->GetConnection();    

    Logi( "Got Connection from :{},port:{},use cnt:{}", client->PeerName(), client->PeerPort(), client.use_count());

    //继续监听
    AsyncAccept();
}

3. 检查ClientBridge的资源管理

长时间运行后无法接受连接,大概率是ClientBridge未正确释放资源,导致文件句柄耗尽或io_context被大量未完成任务占用。需要确保:

  • ClientBridge中异步读写操作完成后,及时释放socket等资源,避免内存和句柄泄漏;
  • client->GetConnection()启动的异步IO操作,其回调函数要正确处理错误,连接关闭时彻底清理相关资源。

4. 防止io_context无任务时退出

可以给io_context添加work对象,确保即使所有异步任务完成,run()也会持续阻塞等待新任务:
在ClientManager中添加成员变量:

boost::asio::io_context::work _work;

在Init函数中初始化:

_work = boost::asio::io_context::work(_ioContext);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 05:02:41