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
疑问
- 若服务器针对写入消息返回RST,为什么Boost.Asio的write回调报告Success?这是预期行为吗?
- 我应该在读取操作中检测连接问题,还是可以在写入操作中检测?(我认为可以,因为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
相关产品推荐
相关产品推荐

