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

如何使用Boost ASIO实现TCP IPC协议并解决连接被重置问题

问题排查与修复方案

核心错误点

  1. 空指针访问直接触发服务端崩溃
    ServerConnection类中定义的properties_是propertiesPacket*类型,初始化值为nullptr,调用async_read_some时直接把空指针作为缓冲区传入,收到客户端数据后会直接写入非法内存,触发服务端进程崩溃,自然会向客户端发送RST包,你看到的connection was reset by the peer就是这么来的。同理filePacket_初始化也为nullptr,如果processData中没有正确分配内存,写入时也会触发同样的问题。

  2. async_read_some使用不符合需求
    TCP是流式协议,async_read_some只保证至少读1字节就返回,不能保证一次读完你需要的完整2字节propertiesPacket结构,大概率会出现收到半包就进入处理逻辑的情况。要固定读取指定长度的数据,应该用boost::asio::async_read接口。

  3. 连续调用异步写入属于未定义行为
    Boost.ASIO要求同一个socket上不能同时存在多个未完成的异步写操作,你在doRead回调里连续调用两次doWrite,前一个异步写还没完成就发起第二个,会导致数据错乱甚至程序崩溃。正确的做法是第一个写操作完成后,在它的回调里再发起第二个写操作。

  4. 写入完成后的逻辑错误
    你的会话流程写完两段数据后就应该断开连接,但是你在doWrite的回调里又调用了doRead,这时候客户端已经完成数据读取会主动关闭连接,服务端此时发起读操作会报错,而且也不符合你的会话设计。

  5. 结构体写入长度错误
    你发送processedData时用了sizeof(*filePacket_)作为长度,这是编译期计算的固定值,但processedData是可变大小结构体,实际长度应该用你提前计算好的filePacketSize_,会导致少发或者多发数据。

  6. 缺少字节序兼容处理(可选但建议修复)
    2字节的长度字段在网络传输时应该统一转成大端(网络字节序),客户端收到后再转成主机字节序,避免不同CPU架构的设备通信时出现长度解析错误。

修复后的服务端核心代码示例

// 修改后的ServerConnection类
class ServerConnection
    : public std::enable_shared_from_this<ServerConnection>
{
public:
    using TCPSocket = boost::asio::ip::tcp::socket;

    ServerConnection(TCPSocket socket)
      : socket_(std::move(socket))
    {
        // 给properties分配实际内存,不再用空指针
        properties_ = std::make_unique<propertiesPacket>();
    }

    void start() { doReadProperties(); }

private:
    void doReadProperties()
    {
        auto self(shared_from_this());
        // 用async_read保证读满整个propertiesPacket的大小
        boost::asio::async_read(socket_, 
            boost::asio::buffer(properties_.get(), sizeof(propertiesPacket)),
            [this, self](boost::system::error_code ec, std::size_t /*length*/)
            {
                if (!ec) {
                    processData();
                    // 先写长度字段
                    doWriteSize();
                }
            });
    }

    void doWriteSize()
    {
        auto self(shared_from_this());
        // 转网络字节序
        unsigned short netSize = htons(filePacketSize_);
        boost::asio::async_write(socket_, 
            boost::asio::buffer(&netSize, sizeof(unsigned short)),
            [this, self](boost::system::error_code ec, std::size_t /*length*/)
            {
                if (!ec) {
                    // 长度写完再写数据体
                    doWriteData();
                }
            });
    }

    void doWriteData()
    {
        auto self(shared_from_this());
        boost::asio::async_write(socket_, 
            boost::asio::buffer(filePacket_.get(), filePacketSize_),
            [this, self](boost::system::error_code ec, std::size_t /*length*/)
            {
                if (!ec) {
                    // 所有数据写完,主动关闭连接即可,不需要再读
                    boost::system::error_code ignore_ec;
                    socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignore_ec);
                    socket_.close(ignore_ec);
                }
            });
    }

    void processData()
    {
        // 这里处理properties_的数据,给filePacket_分配内存,计算filePacketSize_
        // 示例:filePacket_ = std::unique_ptr<processedData>(reinterpret_cast<processedData*>(malloc(filePacketSize_)));
    }

    TCPSocket socket_;
    // 改用智能指针管理内存,避免野指针和内存泄漏
    std::unique_ptr<propertiesPacket> properties_;
    std::unique_ptr<processedData> filePacket_;
    unsigned short filePacketSize_ = 0;
};

客户端补充建议

客户端读取长度的时候也需要转成主机字节序:

unsigned short responseSize {};
boost::asio::read(socket, boost::asio::buffer(&responseSize, sizeof(unsigned short)));
// 加一行转主机字节序
responseSize = ntohs(responseSize);

内容的提问来源于stack exchange,提问作者Spiwocoal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 11:21:03