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; } }
其他可能问题排查
- 串口参数不完整:仅设置波特率可能不够,需确保数据位、停止位、奇偶校验和设备一致,比如常见的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));
- 串口打开校验:检查输出是否有
successfully,确认串口成功打开;如果打开失败,m_serial可能为空,调用async_read_some会崩溃。 io_context生命周期:主线程中io的生命周期足够,但线程捕获引用[&io]有悬空风险,建议改为捕获值[io]()。
内容的提问来源于stack exchange,提问作者ups9527
相关产品推荐
相关产品推荐

