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

Boost ASIO异步TCP服务返回超65536字节响应报Connection reset by peer

问题根因

你遇到的固定65536字节处触发连接重置的问题,是两个核心逻辑错误共同导致的,和Linux系统默认TCP发送缓冲区大小刚好为64KB的特征完全匹配:

  • 你对boost::asio::async_write的完成语义存在误解:该接口的完成回调触发,仅代表所有响应数据已经从用户态内存拷贝到操作系统内核的TCP发送缓冲区,不代表数据已经被网卡发出,更不代表数据已经被客户端成功接收。
  • 连接生命周期管理和socket操作逻辑存在严重缺陷:
    1. 服务端从未调用任何读接口接收curl发来的HTTP请求报文,这些数据一直滞留在socket的内核接收缓冲区中,从未被应用层读取。
    2. handle_write是空实现,回调执行完成后,没有任何新的异步操作绑定到当前连接,tcp_connection实例的智能指针引用计数直接降到0,socket会在对象析构时被强制调用close()。
    3. 根据TCP协议栈的默认实现,调用close()关闭socket时,如果内核接收缓冲区存在未被读取的数据,内核不会走正常的四次挥手流程,会直接丢弃发送缓冲区中还没来得及发出的剩余数据,向客户端发送RST重置包,这就是curl返回curl: (56) Recv failure: Connection reset by peer的根本原因。

偶现完整返回响应的场景也符合逻辑:当网络延迟极低时,内核在socket被关闭前就已经把全部100KB响应发送完成并收到客户端ACK,未触发RST逻辑。你之前尝试的拆分写入块、主动调用close/shutdown、修改socket标志位等方案无效,本质都是没有解决「未读接收缓冲区数据+写入完成后立刻析构连接」两个核心问题。

修复方案

需要做三个核心调整:

  1. 不要忽略handle_write中的错误码参数,先判断写入过程是否出现异常。
  2. 所有响应数据写入内核完成后,调用shutdown关闭socket的发送方向,通知内核应用层已经没有数据要发送,触发内核正常发送缓冲区剩余数据和FIN报文。
  3. 发起一个轻量的异步读操作:一方面读走滞留在内核接收缓冲区的客户端请求数据(哪怕直接丢弃不需要解析),另一方面通过绑定shared_from_this()持有连接的智能指针引用,直到收到客户端的FIN(连接正常关闭)或者连接错误,再让连接对象自然析构。
修复后的核心代码

首先在tcp_connection类中新增读缓冲区和读回调,修改写入完成逻辑:

class tcp_connection
  : public boost::enable_shared_from_this<tcp_connection>
{
// ... 原有公有方法保持不变
private:
  // 原有私有方法和成员保留,新增读回调和读缓冲区
  void handle_read(const boost::system::error_code& /*ec*/, size_t /*bytes_transferred*/)
  {
    // 不需要处理读取到的内容,只要等待连接关闭/出错即可
    // 函数返回后无新的异步操作,智能指针引用计数归零,连接自动安全释放
  }

  void handle_write(const boost::system::error_code& ec,
      size_t /*bytes_transferred*/)
  {
    if (ec) {
      // 写入异常直接返回,连接会自动析构
      return;
    }
    // 关闭发送方向,触发正常TCP挥手流程
    boost::system::error_code ignored_ec;
    socket_.shutdown(tcp::socket::shutdown_send, ignored_ec);

    // 发起异步读,清空接收缓冲区,持有连接引用直到关闭
    boost::asio::async_read(socket_, boost::asio::buffer(&read_buf_, 1),
      boost::asio::transfer_at_least(0),
      boost::bind(&tcp_connection::handle_read, shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));
  }

  tcp::socket socket_;
  std::string message_;
  char read_buf_; // 1字节读缓冲区即可,不需要存储完整请求
};

替换后重新编译运行,就不会再出现固定64KB处连接重置的问题。如果要实现符合规范的HTTP服务,建议在发送响应前先完整读取并解析客户端的HTTP请求,逻辑会更严谨。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 08:09:20