Using boost::asio::async_wait_until with boost::asio::stream

Using boost::asio::async_wait_until with boost::asio::streambuf

本文关键字:asio boost with stream until async Using wait      更新时间:2023-10-16

我目前正在开发一个应用程序,用于使用串行通信与设备进行通信。 为此,我正在使用提升库basic_serial_port。 现在,我只是尝试从设备读取,并使用async_wait_until函数以及deadline_timer类的async_wait。 设置读取和等待的代码如下所示:

async_read_until(port,readData,io_params.delim,
boost::bind(&SerialComm::readCompleted,
this,boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
timer.expires_from_now(boost::posix_time::seconds(1));
timer.async_wait(boost::bind(&SerialComm::timeoutExpired,this,
boost::asio::placeholders::error));

async_read_until上的回调看起来像

void SerialComm::readCompleted(const boost::system::error_code& error,
const size_t bytesTransferred){
if (!error){
wait_result = success;
bytes_transferred = bytesTransferred;
}
else {
if (error.value() != 125) wait_result = error_out;
else wait_result = op_canceled;
cout << "Port handler called with error code " + to_string(error.value()) << endl;
}
}

并且以下代码在成功读取时触发

string msg;
getline(istream(&readData), msg, 'r');
boost::trim_right_if(msg, boost::is_any_of("r"));

对于此设备,所有消息都以回车符终止,因此在async_read_until中指定回车符应检索单个消息。但是,我所看到的是,当处理程序被触发时,新数据不一定输入到缓冲区中。 所以,我可能会看到的是,如果处理程序被触发 20 倍

  • 在第一次调用中将一行泵入缓冲区
  • 在接下来的 6 次调用中无
  • 下次通话 6 行
  • 接下来的 10 年没有数据
  • 以下 10 行 ...

我显然没有做正确的事情,但它是什么?

async_read_until

不保证它只读取到第一个分隔符。

由于底层实现细节,它只会在大多数系统上"读取可用内容",如果 streambuf 包含分隔符,它将返回。其他数据将在 streambuf 中。此外,即使您还没有预料到,EOF也可能被退回

请参阅背景 阅读直到 boost::asio::streambuf 中的字符串分隔符

所以,在这里找到了问题。 该程序旨在工作的方式是它应该

  1. 发送数据请求
  2. 启动async_read_until以读取端口上的数据。
  3. 开始一个async_wait,这样我们就不会永远等待。
  4. 使用io_service::run_one等待超时读取成功。

第四步的代码如下所示

for (;;){
// This blocks until an event on io_service_ is set.
n_handlers = io_service_.run_one();

// Brackets in success case limit scope of new variables
switch(wait_result){
case success:{
char c_[1024];
//string msg;
string delims = "r";
std::string msg{buffers_begin(readData.data()), buffers_begin(readData.data()) + bytes_transferred- delims.size()};
// Consume through the first delimiter.
readData.consume(bytes_transferred);
data_out = msg;
cout << msg << endl;
data_handler(msg);
return data_out;
}
case timeout_expired:
//Set up for wait and read.
wait_result = in_progress;
cout << "Time is up..." << endl;
return data_out;
break;
case error_out:
cout << "Error out..." << endl;
return data_out;
break ;
case op_canceled:
return data_out;
break;
case in_progress:
cout << "In progress..." << endl;
break;
}
}

只有两种情况应触发循环的退出 -timeout_expiredsuccess。 但是,如您所见,如果操作被取消(op_canceled)出现错误(error_out),系统将退出。

问题是当异步操作被取消时(即deadline_timer::cancel()) 它将触发io_service::run_one选取的事件,该事件将 switch 语句评估的状态设置为op_canceled。 这可能会使异步操作在事件循环中堆叠。 简单的解决方法是在successtimeout_expired之外的所有情况下都注释掉 return 语句。