如何从已运行的Boost.Asio服务器中启动第二个服务实例
实现方案
方案1:复用现有主io_context(最优,无需额外线程)
Boost.Asio的io_context支持运行时动态新增异步任务,无法添加新服务器的问题本质是临时服务器实例的生命周期管理不当,导致对象提前析构,异步任务被自动取消。
实现步骤:
- 定义独立的临时服务器类,继承
std::enable_shared_from_this管理生命周期,构造时传入主io_context引用和原有长连接session的弱引用 - 构造临时服务器时直接绑定随机端口(端口传0即可),启动
async_accept异步监听逻辑,无需额外启动操作,现有正在运行的io_context会自动调度该异步任务 - 关停逻辑封装为
stop方法,内部通过asio::post投递到io_context线程执行,先关闭acceptor取消所有监听任务,再通过弱引用获取原有session实例,调用其写接口发送关停信号 - 在
session类中新增shared_ptr<temp_server>类型的成员持有临时服务器实例,避免do_write返回后临时服务器被自动析构
关键代码示例:
// 临时服务器类定义 class temp_server : public std::enable_shared_from_this<temp_server> { public: temp_server(asio::io_context& io_ctx, std::weak_ptr<session> owner_session) : acceptor_(io_ctx, tcp::endpoint(tcp::v4(), 0)), // 0表示系统随机分配可用端口 owner_session_(std::move(owner_session)) { do_accept(); // 如需获取实际监听端口可调用:acceptor_.local_endpoint().port() } // 外部调用关停接口 void stop() { asio::post(acceptor_.get_executor(), [self = shared_from_this()]() { std::error_code ignore_ec; self->acceptor_.close(ignore_ec); // 向原有长连接发送关停信号 if (auto sess = self->owner_session_.lock()) { sess->do_write("temp_server_stopped"); } }); } private: void do_accept() { acceptor_.async_accept([self = shared_from_this()](std::error_code ec, tcp::socket peer_sock) { if (!ec) { // 此处添加临时服务的连接处理逻辑 // 继续监听下一个连接 self->do_accept(); } }); } tcp::acceptor acceptor_; std::weak_ptr<session> owner_session_; }; // session类中do_write方法启动临时服务的逻辑 class session : public std::enable_shared_from_this<session> { // ...原有类成员省略 private: std::shared_ptr<temp_server> temp_server_inst_; void do_write(std::size_t length) { // ...原有do_write逻辑省略 // 启动临时服务器 temp_server_inst_ = std::make_shared<temp_server>( socket_.get_executor().context(), shared_from_this() ); } // 按需在指定时机调用关停 void trigger_stop_temp_server() { if (temp_server_inst_) { temp_server_inst_->stop(); temp_server_inst_.reset(); // 释放临时服务器资源 } } };
方案2:独立io_context+独立线程实现
如果必须隔离临时服务和主服务的调度逻辑,可使用独立io_context实现,之前失败的核心原因大概率是未正确持有io_context生命周期,或未添加工作守卫导致io_context无任务时自动退出。
实现要点:
- 用
shared_ptr持有独立io_context实例,同时创建asio::executor_work_guard绑定到该io_context,避免其无任务时自动退出 - 启动独立线程调用
io_context::run() - 关停时先调用
io_context::stop(),再join对应工作线程 - 向原有长连接发送消息时,必须通过
asio::post将写操作投递到主io_context线程执行,避免跨线程操作session实例导致数据竞争
注意事项
- 所有Asio对象的方法调用都需在其绑定的
io_context线程执行,跨线程操作必须通过asio::post投递 - 异步回调中必须持有对应实例的
shared_ptr,避免回调触发前实例被析构 - 临时服务器不再使用后及时释放持有的
shared_ptr,避免内存泄漏
内容的提问来源于stack exchange,提问作者Vlad Sahar
相关产品推荐
相关产品推荐

