使用 boost::asio::async_wait_until 和 boost::asio::streambuf
Posted
技术标签:
【中文标题】使用 boost::asio::async_wait_until 和 boost::asio::streambuf【英文标题】:Using boost::asio::async_wait_until with boost::asio::streambuf 【发布时间】:2017-09-02 03:53:50 【问题描述】:我目前正在开发一个应用程序,用于与使用串行通信的设备进行通信。为此,我使用了 boost 库 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
中指定回车应该检索单个消息。但是,我看到的是,在触发处理程序时,新数据不一定会输入缓冲区。所以,我可能看到的是,如果处理程序被触发 20x
我显然没有正确地做某事,但它是什么?
【问题讨论】:
【参考方案1】:所以,这里发现了问题。这个程序的工作方式是它应该
-
发送数据请求
启动
async_read_until
以读取端口上的数据。
启动async_wait
,这样我们就不会永远等待。
使用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 msgbuffers_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_expired
和 success
。但是,如您所见,如果操作被取消(op_canceled
)或如果出现错误(error_out
),系统将退出。
问题在于,当异步操作被取消(即deadline_timer::cancel()
)时,它将触发io_service::run_one
拾取的事件,这会将switch 语句评估的状态设置为op_canceled
。这会使异步操作在事件循环中堆积。简单的解决方法是在 所有 情况下将 return 语句注释掉,success
和 timeout_expired
除外。
【讨论】:
【参考方案2】:async_read_until
不保证它只会读到第一个分隔符。
由于底层的实现细节,它只会在大多数系统上“读取可用的内容”,如果 streambuf 包含分隔符,它将返回。 其他数据将在流缓冲区中。此外,EOF 可能会返回,即使您还没有预料到。
查看背景Read until a string delimiter in boost::asio::streambuf
【讨论】:
对不起 - 我不明白这一点。您链接到的帖子表明,如果我返回的东西看起来像cmd1\r
,我应该期望 async_read_until
将其放入缓冲区。这不是我看到的行为。如上所述,处理程序会触发(即到达分隔符)但没有数据放在缓冲区中(甚至没有流浪\r
)。
我链接到的帖子表明,如果您返回的东西看起来像 cmd1\rcmd2\rcmd3\rcmd4\rcmd5\r
,我可以期望 async_read_until
将 cmd1\rcm
放在该缓冲区中。或cmd1\rcmd2\rcmd3\rc
。或者实际上,如果您在读取时已经获得完整数据。
根据您管理流缓冲区的确切方式,这可以解释您似乎阅读“没有新数据”(它已经存在)。当然,直到整个缓冲区被消耗并且您进行实际的新读取。如果您将帖子设为 SSCCE,我将向您展示固定样本。
SSCCE?也许我只是我只是误会。以下是事件的顺序:1)我发送数据请求,2)我将async_wait_until
设置为读取,3)我将计时器设置为超时,4)当处理程序触发时,我们设置一个标志来读取缓冲区,5)我们尝试从缓冲区中读取新数据。似乎正在发生的事情(但也许我只是不明白boost::asio::streambuf
)是新数据被洗牌到前面。但是我会定期在缓冲区中得到垃圾,这从一开始的空字节就可以看出。我应该期望新数据加载到缓冲区的前面吗?
你有谷歌吗? SSCCE 表示Simple Selfcontained Complete Correct Example。你当然需要一个。它可能会让你自己看到问题。如果没有,我会帮忙的。以上是关于使用 boost::asio::async_wait_until 和 boost::asio::streambuf的主要内容,如果未能解决你的问题,请参考以下文章
在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?
Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)