Boost ASIO异步TCP服务返回超65536字节响应报Connection reset by peer
问题根因
你遇到的固定65536字节处触发连接重置的问题,是两个核心逻辑错误共同导致的,和Linux系统默认TCP发送缓冲区大小刚好为64KB的特征完全匹配:
- 你对
boost::asio::async_write的完成语义存在误解:该接口的完成回调触发,仅代表所有响应数据已经从用户态内存拷贝到操作系统内核的TCP发送缓冲区,不代表数据已经被网卡发出,更不代表数据已经被客户端成功接收。 - 连接生命周期管理和socket操作逻辑存在严重缺陷:
- 服务端从未调用任何读接口接收curl发来的HTTP请求报文,这些数据一直滞留在socket的内核接收缓冲区中,从未被应用层读取。
handle_write是空实现,回调执行完成后,没有任何新的异步操作绑定到当前连接,tcp_connection实例的智能指针引用计数直接降到0,socket会在对象析构时被强制调用close()。- 根据TCP协议栈的默认实现,调用
close()关闭socket时,如果内核接收缓冲区存在未被读取的数据,内核不会走正常的四次挥手流程,会直接丢弃发送缓冲区中还没来得及发出的剩余数据,向客户端发送RST重置包,这就是curl返回curl: (56) Recv failure: Connection reset by peer的根本原因。
偶现完整返回响应的场景也符合逻辑:当网络延迟极低时,内核在socket被关闭前就已经把全部100KB响应发送完成并收到客户端ACK,未触发RST逻辑。你之前尝试的拆分写入块、主动调用close/shutdown、修改socket标志位等方案无效,本质都是没有解决「未读接收缓冲区数据+写入完成后立刻析构连接」两个核心问题。
修复方案
需要做三个核心调整:
- 不要忽略
handle_write中的错误码参数,先判断写入过程是否出现异常。 - 所有响应数据写入内核完成后,调用
shutdown关闭socket的发送方向,通知内核应用层已经没有数据要发送,触发内核正常发送缓冲区剩余数据和FIN报文。 - 发起一个轻量的异步读操作:一方面读走滞留在内核接收缓冲区的客户端请求数据(哪怕直接丢弃不需要解析),另一方面通过绑定
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
相关产品推荐
相关产品推荐

