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

使用Boost::asio async_read时程序崩溃及超时问题求助

Boost.Asio TCP通信async_read崩溃及超时逻辑异常问题

问题背景

基于Boost::asio开发TCP服务器,服务器与telnet客户端连接成功后,async_write可正常向客户端发送数据,但调用async_read读取客户端数据时程序崩溃,推测问题出在缓冲区但无法定位。后续发现readResponse函数未等待10秒超时就直接抛出超时异常,测试确认计时器触发但IO回调被阻塞,函数直接跳转到条件判断返回。代码由之前的串口通信项目修改而来,串口通信场景下无此问题。

相关代码

session::readResponse函数

std::vector<char> session::readResponse(const int move_id)
{
    
    int data_size;
    switch (move_id)
    {
        // receive data from info1
        case 1: 
            data_size = 20;
            break;
        // receive data from info2
        case 2: 
            data_size = 19;
            break;
        default:
            data_size = 0;
            break;
    }

    auto self(shared_from_this());
    bool data_available = false;
    std::vector<char> char_vector(data_size);

    try
    {
        boost::mutex::scoped_lock scoped_locker(*mutex_);
        boost::asio::async_read( 
            socket_, 
            boost::asio::buffer(char_vector, data_size),
            [&](const boost::system::error_code &error, std::size_t bytes_transferred)
            {
                if (error)
                {
                    data_available = false;
                    std::cerr << ">>> readCallback Error " << error << std::endl;
                }
                timeout_->cancel();
                data_available = true;
            });
        
        timeout_->expires_from_now(boost::posix_time::seconds(10));
        timeout_->async_wait(  
            [&](const boost::system::error_code &error)
            {
                if (!error)
                {
                    data_available = false;
                    socket_.cancel();
                    std::cerr << ">>> Read timeout." << std::endl;
                }
            });
    }
    catch(const std::exception& e)
    {
        std::cerr << ">>> Read exception. " << e.what() << '\n';
    }
    
    if (data_available)
    {
        return char_vector;
    }
    else
    {
        throw ">>> reading timeout";
    }

}

gManualSendCommand函数

void gManualSendCommand(Server& srv)
{
    while( true ) 
        {
            if(srv.getSession() == NULL)
            {
            }
            else
            {
                std::cout << "Enter robot command id: ";
                std::string id;
                std::getline(std::cin, id);
                srv.getSession()->writeCommand(std::stoi(id));
                srv.getSession()->readResponse(std::stoi(id));
                std::cout << "\n";
            }
            
        }
    
}

问题分析与修复方案

核心问题

  1. 异步操作同步化错误:readResponse试图用异步的async_read/async_wait实现同步读取,但Boost.Asio异步操作是非阻塞的,函数发起异步调用后会直接执行到末尾的条件判断,此时data_available尚未被回调修改,导致直接抛出超时异常。
  2. 局部变量悬空引用:两个lambda回调通过[&]捕获局部变量(data_available、socket_等),但readResponse执行完毕后局部变量会被销毁,回调再访问这些引用会触发未定义行为,这是程序崩溃的根本原因。

修复方案

方案1:改用同步读取+超时(简单直接)

若无需异步并发优势,可使用同步read结合定时器实现超时:

std::vector<char> session::readResponse(const int move_id)
{
    int data_size;
    switch (move_id)
    {
        case 1: data_size = 20; break;
        case 2: data_size = 19; break;
        default: data_size = 0; break;
    }

    std::vector<char> char_vector(data_size);
    boost::system::error_code ec;

    // 设置超时定时器
    timeout_->expires_from_now(boost::posix_time::seconds(10));
    timeout_->async_wait([this](const boost::system::error_code& error) {
        if (!error) socket_.cancel();
    });

    // 同步读取
    boost::asio::read(socket_, boost::asio::buffer(char_vector, data_size), ec);
    
    // 取消定时器
    timeout_->cancel();

    if (ec == boost::asio::error::operation_aborted) {
        throw ">>> reading timeout";
    } else if (ec) {
        std::cerr << ">>> Read Error " << ec << std::endl;
        throw ">>> read failed";
    }

    return char_vector;
}

方案2:正确使用异步模型(符合Asio设计理念)

若需保留异步操作,需将读取逻辑改为异步回调链式调用,避免局部变量悬空:

// 修改为异步接口,通过回调返回结果
void session::readResponse(const int move_id, std::function<void(std::vector<char>, bool)> callback)
{
    int data_size;
    switch (move_id)
    {
        case 1: data_size = 20; break;
        case 2: data_size = 19; break;
        default: data_size = 0; break;
    }

    auto self = shared_from_this();
    auto buffer = std::make_shared<std::vector<char>>(data_size);

    timeout_->expires_from_now(boost::posix_time::seconds(10));
    timeout_->async_wait([self, callback](const boost::system::error_code& error) {
        if (!error) {
            self->socket_.cancel();
            std::cerr << ">>> Read timeout." << std::endl;
            callback({}, false);
        }
    });

    boost::asio::async_read(self->socket_, boost::asio::buffer(*buffer, data_size),
        [self, buffer, callback](const boost::system::error_code& error, std::size_t bytes_transferred) {
            self->timeout_->cancel();
            if (error) {
                std::cerr << ">>> readCallback Error " << error << std::endl;
                callback({}, false);
                return;
            }
            callback(std::move(*buffer), true);
        });
}

同时修改调用逻辑:

void gManualSendCommand(Server& srv)
{
    while( true ) 
    {
        if(srv.getSession() == NULL)
        {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            continue;
        }
        
        std::cout << "Enter robot command id: ";
        std::string id;
        std::getline(std::cin, id);
        int cmd_id = std::stoi(id);
        
        srv.getSession()->writeCommand(cmd_id);
        // 异步回调处理读取结果
        srv.getSession()->readResponse(cmd_id, [](std::vector<char> data, bool success) {
            if (success) {
                std::cout << "Read data: " << std::string(data.begin(), data.end()) << std::endl;
            } else {
                std::cerr << "Read failed or timeout" << std::endl;
            }
        });
        
        std::cout << "\n";
    }
}

额外注意事项

  • 确保session对象通过shared_ptr管理(已使用shared_from_this(),符合要求),避免回调触发时对象已销毁。
  • 同步读取场景下需保证线程安全,避免同一socket被多线程同时操作。
  • 串口通信因模型差异(如默认阻塞读取)未暴露问题,但TCP异步模型需严格遵循Asio异步编程范式。

内容的提问来源于stack exchange,提问作者RyanChen.YLC

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 23:03:25