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

使用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()方法中:

  1. 修改SocketConnection.cpp的构造函数:
SocketConnection::SocketConnection(boost::asio::ip::tcp::socket socket)
    : _socket(std::move(socket))
{
  // 移除do_read()调用
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 19:25:53