Boost Asio TCP客户端重连后读取数据长度为0的问题
Boost Asio TCP客户端重连后读取缓冲区长度为0的问题
问题描述
我使用Boost Asio连接TCP服务端,程序初始运行时可正常发送请求并接收响应。当关闭TCP服务端(为一台设备)后,出现读取超时情况,此时boost::async_read的回调函数始终未执行,随后我关闭了socket。重新开启设备并重连成功后,读取到的接收缓冲区长度为0字节,推测该问题是由于超时后async_read未正确结束导致。
头文件
#include <iostream> #include <boost/format.hpp> #include <boost/asio.hpp> #include <boost/bind.hpp> #include <chrono> #include <thread> #include <future> #include <sstream> #include <iomanip> class TcpClient{ public: int connect(boost::asio::ip::tcp::socket &socket, boost::asio::ip::tcp::endpoint &endpoint); int writeAndRead(boost::asio::ip::tcp::socket &socket); };
实现代码
#include "tcpclient.h" int TcpClient::connect(boost::asio::ip::tcp::socket &socket, boost::asio::ip::tcp::endpoint &endpoint) { boost::system::error_code error; socket.connect(endpoint, error); if (!error) { std::cout << "connected" << std::endl; return 1; } else { std::cout << "not connected" << std::endl; return 0; } } int TcpClient::writeAndRead(boost::asio::ip::tcp::socket &socket) { boost::system::error_code error; auto status = std::async(std::launch::async, [&]() { boost::asio::write(socket, boost::asio::buffer("mytext"), error); }) .wait_for(std::chrono::milliseconds{1000}); switch (status) { case std::future_status::deferred: std::cout << "std::future_status::deferred" << std::endl; return 0; case std::future_status::ready: std::cout << "write success" << std::endl; break; case std::future_status::timeout: std::cout << "std::future_status::timeout" << std::endl; return 0; } boost::asio::streambuf receive_buffer; boost::optional<boost::system::error_code> read_result; boost::optional<boost::system::error_code> timer_result; boost::asio::deadline_timer timer(socket.get_io_service()); timer.expires_from_now(boost::posix_time::seconds(2)); timer.async_wait([&timer_result](const boost::system::error_code &error) { if (error != boost::asio::error::operation_aborted) { timer_result = error; } }); boost::asio::async_read(socket, receive_buffer, boost::asio::transfer_at_least(1), [&read_result](const boost::system::error_code &ec, std::size_t bytes_transferred) { std::cout << "read_result: " << read_result << std::endl; read_result = ec; }); boost::system::error_code ec; while (1) { socket.get_io_service().reset(); int numHandlers = socket.get_io_service().poll_one(ec); if (read_result) { timer.cancel(); break; } else if (timer_result) { timer.cancel(); std::cout << "timeout" << std::endl; return 0; } } if (receive_buffer.size() == 0) { std::cout << "receive_buffer size 0" << std::endl; return 0; } std::string rawResponse = boost::asio::buffer_cast<const char *>(receive_buffer.data()); std::cout << "rawResponse: " << rawResponse << std::endl; return 1; } int main() { std::string ipAddress = "192.168.2.4"; unsigned short port = 50001; boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(ipAddress), port); boost::asio::io_service ioService; boost::asio::ip::tcp::socket socket{ioService}; bool isSuccess{false}; bool isConnected{false}; TcpClient tcpclient = TcpClient(); while (1) { if (!isConnected) { isConnected = tcpclient.connect(socket, endpoint); } if (isConnected) { isSuccess = tcpclient.writeAndRead(socket); if (!isSuccess) { std::cout << "failed close socket" << std::endl; socket.close(); isConnected = false; } else { std::cout << "success" << std::endl; } } std::cout << "wait for 1 sec" << std::endl; std::chrono::seconds dura(1); std::this_thread::sleep_for(dura); } return 0; }
程序输出
成功 等待1秒 写入成功 read_result: 0 rawResponse: 0; 成功 等待1秒 写入成功 超时 失败,关闭socket 等待1秒 未连接 等待1秒 未连接 等待1秒 未连接 等待1秒 未连接 等待1秒 已连接 写入成功 read_result: 0 receive_buffer长度为0 失败,关闭socket 等待1秒 已连接 写入成功 read_result: 0 receive_buffer长度为0 失败,关闭socket
问题原因及解决办法
核心问题
- 超时后未取消
async_read操作:读取超时触发时,仅取消了定时器,async_read仍处于挂起状态。关闭socket虽会终止操作,但回调未被正确调度执行,导致后续重连时IO服务残留旧操作状态。 - IO服务管理不当:循环中反复调用
io_service.reset()会打乱服务状态,未处理的异步操作残留会干扰新连接的读取逻辑。 - 回调输出逻辑错误:
async_read回调中输出未初始化的read_result,导致错误的状态提示。
修复步骤
1. 超时后取消socket上的所有异步操作
在超时分支中添加socket.cancel(),确保async_read回调被触发并清理状态:
else if (timer_result) { timer.cancel(); socket.cancel(); // 取消socket上的所有异步操作 std::cout << "timeout" << std::endl; socket.get_io_service().poll(); // 等待回调执行完成 return 0; }
2. 重连时重置IO服务与socket
关闭socket后重新创建IO服务和socket对象,彻底清除旧状态:
if (!isSuccess) { std::cout << "failed close socket" << std::endl; socket.close(); ioService.reset(); socket = boost::asio::ip::tcp::socket{ioService}; // 重新创建socket isConnected = false; }
3. 修正回调输出逻辑
在async_read回调中输出实际的错误码信息,避免误导:
[&read_result](const boost::system::error_code &ec, std::size_t bytes_transferred) { std::cout << "read_result: " << ec.message() << std::endl; read_result = ec; }
内容的提问来源于stack exchange,提问作者otto
相关产品推荐
相关产品推荐

