实例化socket是否属于不良实践?C++ Asio服务端单例套接字无响应求助
问题诊断
- 构造函数阻塞无法返回:
ServerSocket构造函数内包含for(;;)无限循环,调用GetInstance()触发构造时会永远卡在循环中,后续代码完全无法执行,自然无法执行后续的写入逻辑,只有在循环内accept后立刻写数据才能生效。 - 核心对象生命周期错误:
asio::io_context是构造函数内的局部变量,即便构造函数能退出,io_context会被立刻销毁,所有依赖它的socket、acceptor都会直接失效。 - 成员指针未初始化:循环内的
tcp::socket是局部变量,循环结束就会被销毁,类成员的socket指针从未被赋值,返回的是空指针/野指针,后续write操作会直接触发崩溃。 - 单例设计不符合服务端逻辑:TCP服务端需要支持多客户端连接,每个连接对应独立的
socket对象,全局仅保留一个socket的设计根本无法处理多连接场景。 - 调用逻辑错误:
SendUser中定义的ServerSocket* instance未初始化就调用方法,属于野指针访问,存在崩溃风险。
修复方案
- 调整核心对象的生命周期:将
io_context、acceptor改为类成员变量,保证其生命周期和单例一致。 - 拆分监听逻辑:将
accept死循环放到独立线程中执行,避免阻塞构造函数返回。 - 存储客户端连接:新增线程安全的容器存储所有接入的客户端
socket,而非仅保留单个指针。 - 修正调用逻辑:直接通过类名调用静态的
GetInstance()方法,避免野指针问题。
修复后代码示例
头文件定义
#include <asio.hpp> #include <iostream> #include <vector> #include <mutex> #include <thread> #include <memory> using asio::ip::tcp; class ServerSocket { public: ServerSocket(ServerSocket& otherSingleton) = delete; void operator=(const ServerSocket& copySingleton) = delete; static ServerSocket* GetInstance(); // 发送数据到最新接入的客户端,可根据需求修改为指定客户端发送 void SendData(const std::string& data); private: static ServerSocket* instance; static std::mutex instanceMutex; asio::io_context io_context; tcp::acceptor acceptor; std::vector<std::unique_ptr<tcp::socket>> connections; std::mutex connMutex; std::thread acceptThread; ServerSocket(); ~ServerSocket(); void AcceptLoop(); };
cpp实现
ServerSocket* ServerSocket::instance = nullptr; std::mutex ServerSocket::instanceMutex; ServerSocket* ServerSocket::GetInstance() { std::lock_guard<std::mutex> lock(instanceMutex); if (instance == nullptr) { instance = new ServerSocket(); } return instance; } ServerSocket::ServerSocket() : acceptor(io_context, tcp::endpoint(tcp::v4(), 13)) { std::cout << "Server started" << std::endl; // 启动独立线程跑监听逻辑 acceptThread = std::thread(&ServerSocket::AcceptLoop, this); } ServerSocket::~ServerSocket() { io_context.stop(); if (acceptThread.joinable()) { acceptThread.join(); } std::cout << "Server closed" << std::endl; } void ServerSocket::AcceptLoop() { try { for (;;) { auto newSocket = std::make_unique<tcp::socket>(io_context); acceptor.accept(*newSocket); std::lock_guard<std::mutex> lock(connMutex); connections.push_back(std::move(newSocket)); } } catch (std::exception& e) { std::cerr << e.what() << std::endl; } } void ServerSocket::SendData(const std::string& data) { std::lock_guard<std::mutex> lock(connMutex); if (connections.empty()) { std::cerr << "No client connected" << std::endl; return; } // 示例是发给最新接入的客户端,可根据需求修改为遍历所有连接发送 asio::error_code ignored_error; asio::write(*connections.back(), asio::buffer(data), ignored_error); }
SendUser逻辑修改
void DBUser::SendUser() { try { ServerSocket::GetInstance()->SendData(m_user); } catch (std::exception& e) { std::cerr << e.what() << std::endl; } }
内容的提问来源于stack exchange,提问作者Vasile Mihai
相关产品推荐
相关产品推荐

