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

Boost.Asio async_write断连后未触发错误的问题咨询

TCP客户端Boost.Asio连接异常问题

问题背景

客户端代码如下:

#include <cstdlib>
#include <deque>
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include "chat_message.hpp"

using boost::asio::ip::tcp;

typedef std::deque<chat_message> chat_message_queue;

class chat_client
{
public:
    chat_client(boost::asio::io_context& io_context,
                const tcp::resolver::results_type& endpoints)
        : io_context_(io_context),
        socket_(io_context),
        worker_{std::make_shared<boost::asio::io_service::work>(io_context)}
    {
        do_connect(endpoints);
    }

    void write(const chat_message& msg)
    {
        std::cerr << "write" << std::endl;
        do_write(msg);
    }

    void close()
    {
        std::cout << "boost::asio::post close" << std::endl;
        boost::asio::post(io_context_, [this]() {
            std::cout << "socket.close" << std::endl;
           socket_.close();
        });
    }

private:
    void do_connect(const tcp::resolver::results_type& endpoints)
    {
        boost::asio::async_connect(socket_, endpoints,
                                   [this](boost::system::error_code ec, tcp::endpoint)
                                   {
                                       if (ec)
                                       {
                                           std::cerr << __LINE__ << " | connect handler " << ec.message() << std::endl;
                                       }
                                   });
    }

    void do_read_header()
    {
        std::cout << "do_read_header" << std::endl;
        boost::asio::async_read(socket_,
                                boost::asio::buffer(read_msg_.data(), chat_message::header_length),
                                [this](boost::system::error_code ec, std::size_t /*length*/)
                                {
                                    std::cout << "do_read_header callback" << std::endl;
                                    if (!ec && read_msg_.decode_header())
                                    {
                                        do_read_body();
                                    }
                                    else
                                    {
                                        std::cerr << __LINE__ << " | header read handler " << ec.message() << std::endl;
                                        socket_.close();
                                    }
                                });
    }

    void do_read_body()
    {
        boost::asio::async_read(socket_,
                                boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
                                [this](boost::system::error_code ec, std::size_t /*length*/)
                                {
                                    if (!ec)
                                    {
                                        std::cout.write(read_msg_.body(), read_msg_.body_length());
                                        std::cout << "\n";
                                        // do_read_header();
                                    }
                                    else
                                    {
                                        std::cerr << __LINE__ << " | body read handler" << ec.message() << std::endl;
                                        // socket_.close();
                                    }
                                });
    }

    void do_write(const chat_message& msg)
    {
        boost::asio::async_write(socket_,
                                 boost::asio::buffer(msg.data(), msg.length()),
                                 [this](boost::system::error_code ec, std::size_t length)
                                 {
                                     std::cerr << __LINE__ << " | write handler " << ec.message() << "-" << length << std::endl;
                                     if (!ec)
                                     {
                                         do_read_header();
                                     }
                                 });
    }

private:
    boost::asio::io_context& io_context_;
    tcp::socket socket_;
    std::shared_ptr<boost::asio::io_service::work> worker_;
    chat_message read_msg_;
};

int main(int argc, char* argv[])
{
    try
    {
        if (argc != 3)
        {
            std::cerr << "Usage: chat_client <host> <port>\n";
            return 1;
        }

        boost::asio::io_context io_context;
        auto work = std::make_shared<boost::asio::io_service::work>(io_context);

        tcp::resolver resolver(io_context);
        auto endpoints = resolver.resolve(argv[1], argv[2]);
        chat_client c(io_context, endpoints);

        std::thread t([&io_context](){ io_context.run(); });

        char line[chat_message::max_body_length + 1];
        while (std::cin.getline(line, chat_message::max_body_length + 1))
        {
            chat_message msg;
            msg.body_length(std::strlen(line));
            std::memcpy(msg.body(), line, msg.body_length());
            msg.encode_header();
            c.write(msg);
        }

        c.close();
        t.join();
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}

服务器使用Boost 1.70官方示例中的chat_server.cpp。

复现步骤

  • 运行服务器
  • 启动客户端并建立TCP连接
  • 停止服务器(通过SIGTERM或Ctrl+C)
  • 在客户端命令行输入内容并回车,向已停止的服务器发送消息

当前现象

客户端async_write回调返回Success,但抓包显示服务器回复了RST,抓包轨迹如下:

  • 服务器发送FIN
  • 客户端回复FIN_ACK
  • 客户端发送消息
  • 服务器回复RST

疑问

  1. 若服务器针对写入消息返回RST,为什么Boost.Asio的write回调报告Success?这是预期行为吗?
  2. 我应该在读取操作中检测连接问题,还是可以在写入操作中检测?(我认为可以,因为TCP是流协议,每个操作都应收到ACK)

解答

问题1:为什么async_write回调返回Success

这是预期行为。Boost.Asio的async_write完成回调返回成功,仅表示数据已经被操作系统的TCP内核栈接收并准备发送,并不代表数据已经被对端接收或确认。

在你的场景中,客户端发送消息时,TCP栈已经把数据发出,此时async_write就完成并返回成功;之后服务器返回RST,这个错误不会回溯影响已经完成的async_write操作,而是会在后续的socket操作(比如下一次async_read或async_write)中触发错误。因为TCP的错误通知是异步的,只有当内核处理到RST包后,后续的socket调用才会感知到连接异常。

问题2:连接问题的检测方式

写入操作不一定能立刻检测到连接问题,原因同上:RST可能在当前async_write完成后才到达,此时当前写入已经返回成功。

通常的做法是:

  • 通过读取操作检测:因为TCP连接关闭(包括RST)会触发读操作的错误(比如eof或connection reset by peer),这是检测连接异常最可靠的方式之一,就像你代码中do_read_header回调里处理错误的逻辑。
  • 后续写入操作检测:如果在发送RST之后再执行写入,此时内核已经知道连接异常,后续的async_write会直接返回错误。
  • 主动心跳机制:如果你的业务场景需要快速感知连接状态,可以定期发送心跳包,通过心跳的响应情况来判断连接是否正常,避免依赖被动的错误通知。

内容的提问来源于stack exchange,提问作者Alvaro Denis Acosta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 09:45:25