使用boost::asio构建异步服务器时遭遇std::bad_weak_ptr异常
异步Socket服务器中std::bad_weak_ptr异常问题排查
我用boost::asio构建异步Socket服务器时遇到指针操作问题,已经让SocketConnection类继承std::enable_shared_from_this<SocketConnection>,但仍触发std::bad_weak_ptr异常。
复现情况
执行telnet连接:
$ telnet 127.0.0.1 8080 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. Connection closed by foreign host.
控制台输出错误:
ACCEPT LOOP system:0 GETTING HERE BEFORE CRASH terminate called after throwing an instance of 'std::bad_weak_ptr' what(): bad_weak_ptr Aborted (core dumped)
代码实现
SocketServer.hpp
#pragma once #include <memory> #include <boost/asio.hpp> #include "SocketConnection.hpp" #define SOCKET_PORT 8080 #define BOOST_ASIO_ENABLE_HANDLER_TRACKING 1 // Enable tracking class SocketServer : public std::enable_shared_from_this<SocketServer> { private: boost::asio::io_context _ioc; boost::asio::ip::tcp::acceptor* _acceptor; std::vector<std::weak_ptr<SocketConnection>> _registered; std::mutex _mutex; public: SocketServer(); ~SocketServer(); void accept_loop(); void register_connection(std::weak_ptr<SocketConnection> connection); void broadcast(std::string const &msg); void run(); };
SocketServer.cpp
#include <algorithm> #include <cstdlib> #include <functional> #include <iostream> #include <memory> #include <string> #include <thread> #include <vector> #include "SocketServer.hpp" SocketServer::SocketServer() { _acceptor = new boost::asio::ip::tcp::acceptor(_ioc, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), SOCKET_PORT)); accept_loop(); } SocketServer::~SocketServer() { std::cout << "Server destructor" << std::endl; } void SocketServer::register_connection(std::weak_ptr<SocketConnection> connection) { std::lock_guard<std::mutex> lock(_mutex); // Will be released automatically _registered.push_back(connection); } void SocketServer::accept_loop() { _acceptor->async_accept( [this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) { std::cout << "ACCEPT LOOP " << ec << std::endl; if (!ec) { auto connection = std::make_shared<SocketConnection>(std::move(socket)); std::cout << "Connection accepted from " << connection->get_remote_endpoint() << " (" << ec.message() << ")" << std::endl; register_connection(connection); connection->run(); } accept_loop(); }); } void SocketServer::broadcast(std::string const &msg) { std::lock_guard<std::mutex> lock(_mutex); std::vector<std::weak_ptr<SocketConnection>> active; for (auto &conn : _registered) { auto c = conn.lock(); c->do_write(msg); } } void SocketServer::run() { _ioc.run(); }
SocketConnection.hpp
#pragma once #include <memory> #include <iostream> #include <boost/asio.hpp> #define MAX_BUFFER_LEN 4096 class SocketConnection : public std::enable_shared_from_this<SocketConnection> { private: boost::asio::ip::tcp::socket _socket; char read_buffer[MAX_BUFFER_LEN]; char write_buffer[MAX_BUFFER_LEN]; public: SocketConnection(boost::asio::ip::tcp::socket socket); ~SocketConnection(); boost::asio::ip::tcp::endpoint get_remote_endpoint(); void do_read(); void on_read(); void do_write(std::string msg); void on_write(); void run(); };
SocketConnection.cpp
#include "SocketConnection.hpp" SocketConnection::SocketConnection(boost::asio::ip::tcp::socket socket) : _socket(std::move(socket)) // socket cannot be copied { do_read(); } SocketConnection::~SocketConnection() { std::cout << "SocketConnection destructor" << std::endl; } boost::asio::ip::tcp::endpoint SocketConnection::get_remote_endpoint() { return _socket.remote_endpoint(); } void SocketConnection::do_read() { std::cout << "GETTING HERE BEFORE CRASH" << std::endl; auto self(shared_from_this()); std::cout << "DOES NOT GET HERE" << std::endl; _socket.async_read_some(boost::asio::buffer(std::ref(read_buffer), MAX_BUFFER_LEN), [this, self](boost::system::error_code ec, std::size_t length) { std::cout << "INSIDE DO_READ: " << ec << " " << ec.message() << std::endl; if (!ec) { on_read(); } }); } void SocketConnection::on_read() { std::string s(read_buffer); std::string reply = "REPLY: " + s; do_write(reply); } void SocketConnection::do_write(std::string msg) { auto self(shared_from_this()); strcpy(write_buffer, msg.c_str()); boost::asio::async_write(_socket, boost::asio::buffer(std::ref(write_buffer), strlen(write_buffer)), [this, self](boost::system::error_code ec, std::size_t /*length*/) { std::cout << "INSIDE DO_WRITE: " << ec << std::endl; if (!ec) { on_write(); } }); } void SocketConnection::on_write() { std::string s(write_buffer); } void SocketConnection::run() { std::cout << "Running" << std::endl; }
问题原因
std::enable_shared_from_this的工作逻辑是:当用std::make_shared或std::shared_ptr构造函数创建对象时,会自动初始化内部的weak_ptr。但在对象构造函数执行期间,这个weak_ptr还未完成初始化,此时调用shared_from_this()必然抛出std::bad_weak_ptr异常。
你的代码中,SocketConnection构造函数直接调用了do_read(),而do_read()第一行就是auto self(shared_from_this());——此时对象仍在构造流程中,shared_ptr还未完成对它的管理,因此触发异常。
修复方案
把构造函数里的do_read()调用移到run()方法中:
- 修改SocketConnection.cpp的构造函数:
SocketConnection::SocketConnection(boost::asio::ip::tcp::socket socket) : _socket(std::move(socket)) { // 移除do_read()调用 }
- 修改run()方法:
void SocketConnection::run() { std::cout << "Running" << std::endl; do_read(); // 在shared_ptr初始化完成后启动读操作 }
这样do_read()会在std::make_shared<SocketConnection>执行完成后(即accept_loop里调用connection->run()时)才被调用,此时shared_from_this()能正常获取有效的shared_ptr。
额外优化建议
- 避免裸指针内存泄漏:SocketServer中的
_acceptor用裸指针+new创建,建议替换为std::unique_ptr<boost::asio::ip::tcp::acceptor>自动管理内存。 - 空指针检查:broadcast方法中,
conn.lock()可能返回空指针(对应连接已断开),需先判断if(c)再调用do_write,否则会触发空指针访问。 - buffer使用优化:
boost::asio::buffer无需使用std::ref,直接传数组名即可(数组会自动退化为指针),例如boost::asio::buffer(read_buffer, MAX_BUFFER_LEN)。 - 缓冲区安全:
strcpy(write_buffer, msg.c_str())可能导致缓冲区溢出,建议改用strncpy并手动添加终止符,或直接用std::string替代char数组作为缓冲区。
内容的提问来源于stack exchange,提问作者Mendes
相关产品推荐
相关产品推荐

