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

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

问题原因及解决办法

核心问题

  1. 超时后未取消async_read操作:读取超时触发时,仅取消了定时器,async_read仍处于挂起状态。关闭socket虽会终止操作,但回调未被正确调度执行,导致后续重连时IO服务残留旧操作状态。
  2. IO服务管理不当:循环中反复调用io_service.reset()会打乱服务状态,未处理的异步操作残留会干扰新连接的读取逻辑。
  3. 回调输出逻辑错误: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 11:38:08