如何正确处理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
相关产品推荐
相关产品推荐

