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

Asio standalone serial_port async_read_some技术问题及代码排查请求

Asio独立版串口读取无数据问题排查及疑问解答

问题背景

使用Asio(standalone)的serial_port从设备读取数据,但无法读取到任何字节,同时有三个疑问需要解答,并排查代码问题。

疑问列表

  • async_read_some的第一个参数具体类型是什么?例如std::vector<uint8_t>是否可行?
  • 如何将类成员函数绑定为回调/处理器?
  • 仅确保io_context.run()在后台线程运行且只调用一次async_read_some是否正确?

代码示例

class NmeaSource {
public:
    explicit NmeaSource(std::shared_ptr<asio::io_context> io, const nlohmann::json& settings);
    void parse();
    void handle(const asio::error_code& error, // Result of operation.
        std::size_t bytes_transferred           // Number of bytes read.
    );

private:
    void open();

    std::string m_port;
    int m_baudrate;
    std::shared_ptr<asio::io_context> m_io;
    std::shared_ptr<asio::serial_port> m_serial;
    unsigned char m_readBuffer[1024];
};

NmeaSource::NmeaSource(std::shared_ptr<asio::io_context> io, const nlohmann::json& settings)
    :m_io(io)
{
    try {
        setPort(settings.at("port"));
        setBaudrate(settings.at("baudrate"));
        std::cout << "port: " << m_port << ", baudrate: " << m_baudrate << std::endl;
    }
    catch (nlohmann::json::basic_json::type_error& e1) {
        std::cout << "type is wrong " << e1.what() << std::endl;
        throw e1;
    }
    catch (nlohmann::json::basic_json::out_of_range& e2) {
        std::cout << "key is not found " << e2.what() << std::endl;
        throw e2;
    }
    catch (...)
    {
        std::cout << "unknown error"<< std::endl;
        exit(-1);
    }

    open();

}

void NmeaSource::open()
{
    m_serial = std::make_shared<asio::serial_port>(*m_io);

    asio::error_code ec;
    m_serial->open(m_port, ec);
    if (!ec) {
        asio::serial_port_base::baud_rate baudrate(m_baudrate);
        m_serial->set_option(baudrate);
        std::cout << "successfully" << std::endl;
    }
    else {
        std::cout << "failed " << ec.message() <<std::endl;
    }
}

void NmeaSource::handle(const asio::error_code& error, // Result of operation.
    std::size_t bytes_transferred           // Number of bytes read.
)
{
    if (!error) {
        std::cout << bytes_transferred << " bytes read!" << std::endl;
    }
    else {
        std::cout << error.message() << std::endl;
    }
}

void NmeaSource::parse()
{
    m_serial->async_read_some(
        asio::buffer(m_readBuffer, 1024),
        std::bind(&NmeaSource::handle, this,
            std::placeholders::_1,
            std::placeholders::_2)
    );

}

int main()
{
    auto io = std::make_shared<asio::io_context>();
    std::thread t([&io]() {io->run(); });
    NmeaSource rtk(io, nlohmann::json{ {"port", "COM3"}, {"baudrate", 115200} });
    rtk.parse();
    for (;;)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
    }
}

解答及代码排查

1. async_read_some第一个参数类型

async_read_some的第一个参数是Asio MutableBufferSequence类型,这是一个概念,只要满足可迭代、每个元素是mutable_buffer的序列都可以:

  • 原始数组:比如代码里的unsigned char m_readBuffer[1024],配合asio::buffer(m_readBuffer, 1024)使用
  • std::vector<uint8_t>:完全可行,比如std::vector<uint8_t> buf(1024);,传asio::buffer(buf)即可,Asio会自动处理其内存区域
  • asio::mutable_buffer直接实例:比如asio::mutable_buffer(buf_ptr, buf_size)

2. 类成员函数绑定为回调

你的代码里用std::bind的实现是正确的:

std::bind(&NmeaSource::handle, this,
    std::placeholders::_1,
    std::placeholders::_2)

注意事项:

  • 必须传递this指针,让bind能定位到类实例
  • 用std::placeholders::_1、_2占位符对应回调的error_code和bytes_transferred参数
  • 也可以用C++11+的lambda表达式更直观:
[this](const asio::error_code& ec, std::size_t bytes) {
    this->handle(ec, bytes);
}

3. io_context.run()与async_read_some的调用问题

你的做法不完全正确:

  • io_context.run()在后台线程运行是对的,但如果io_context没有待处理的异步操作,run()会直接返回。你第一次调用async_read_some后,线程会等待操作完成,但操作结束后不会自动发起新的读取。
  • async_read_some是一次性操作:完成一次读取后,不会再监听设备数据。要持续读取,必须在handle回调中再次调用async_read_some,形成循环:
void NmeaSource::handle(const asio::error_code& error, std::size_t bytes_transferred)
{
    if (!error) {
        std::cout << bytes_transferred << " bytes read!" << std::endl;
        // 再次发起异步读取,保持读取循环
        parse();
    }
    else {
        std::cout << error.message() << std::endl;
    }
}

其他可能问题排查

  1. 串口参数不完整:仅设置波特率可能不够,需确保数据位、停止位、奇偶校验和设备一致,比如常见的8N1配置:
m_serial->set_option(asio::serial_port_base::character_size(8));
m_serial->set_option(asio::serial_port_base::parity(asio::serial_port_base::parity::none));
m_serial->set_option(asio::serial_port_base::stop_bits(asio::serial_port_base::stop_bits::one));
  1. 串口打开校验:检查输出是否有successfully,确认串口成功打开;如果打开失败,m_serial可能为空,调用async_read_some会崩溃。
  2. io_context生命周期:主线程中io的生命周期足够,但线程捕获引用[&io]有悬空风险,建议改为捕获值[io]()。

内容的提问来源于stack exchange,提问作者ups9527

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 09:45:42