Boost 如何使用asio执行非阻塞读取?

Boost 如何使用asio执行非阻塞读取?,boost,serial-port,boost-asio,Boost,Serial Port,Boost Asio,我正在尝试使用boost::asio从串行端口上的设备进行读写。当没有可读取的内容时,boost::asio:read()和boost::asio::serial_port::read_some()都会被阻塞。相反,我希望检测到这种情况,并向端口写入命令以启动设备 我如何检测到没有可用的数据 如果有必要,我可以异步地做任何事情,如果可以的话,我宁愿避免额外的复杂性。您必须使用免费函数asio::async\u read。实际上,您有两个选择。您可以使用串口内置的async\u read\u so

我正在尝试使用boost::asio从串行端口上的设备进行读写。当没有可读取的内容时,boost::asio:read()和boost::asio::serial_port::read_some()都会被阻塞。相反,我希望检测到这种情况,并向端口写入命令以启动设备

我如何检测到没有可用的数据


如果有必要,我可以异步地做任何事情,如果可以的话,我宁愿避免额外的复杂性。

您必须使用免费函数asio::async\u read。

实际上,您有两个选择。您可以使用串口内置的
async\u read\u some
功能,也可以使用独立功能
boost::asio::async\u read
(或
async\u read\u some

您仍然会遇到被有效“阻止”的情况,因为除非(1)已读取数据或(2)发生错误,否则它们都不会调用回调。要解决这个问题,您需要使用
deadline\u timer
对象来设置超时。如果先触发超时,则没有可用数据。否则,您将读取数据

增加的复杂性其实并不那么糟糕。您将得到两个行为类似的回调。如果“read”或“timeout”回调出现错误,您就知道是比赛输家。如果其中一个发射没有错误,那么你就知道它是比赛的赢家(你应该取消另一个呼叫)。在对
read\u some
进行阻塞调用的地方,现在将调用
io\u svc.run()
。您的函数在调用
run
时仍会像以前一样阻塞,但这次您可以控制持续时间

下面是一个例子:

void foo()
{
  io_service     io_svc;
  serial_port    ser_port(io_svc, "your string here");
  deadline_timer timeout(io_svc);
  unsigned char  my_buffer[1];
  bool           data_available = false;

  ser_port.async_read_some(boost::asio::buffer(my_buffer),
      boost::bind(&read_callback, boost::ref(data_available), boost::ref(timeout),
                  boost::asio::placeholders::error,
                  boost::asio::placeholders::bytes_transferred));
  timeout.expires_from_now(boost::posix_time::milliseconds(<<your_timeout_here>>));
  timeout.async_wait(boost::bind(&wait_callback, boost::ref(ser_port),
                  boost::asio::placeholders::error));

  io_svc.run();  // will block until async callbacks are finished

  if (!data_available)
  {
    kick_start_the_device();
  }
}

void read_callback(bool& data_available, deadline_timer& timeout, const boost::system::error_code& error, std::size_t bytes_transferred)
{
  if (error || !bytes_transferred)
  {
    // No data was read!
    data_available = false;
    return;
  }

  timeout.cancel();  // will cause wait_callback to fire with an error
  data_available = true;
}

void wait_callback(serial_port& ser_port, const boost::system::error_code& error)
{
  if (error)
  {
    // Data was read and this timeout was canceled
    return;
  }

  ser_port.cancel();  // will cause read_callback to fire with an error
}
void foo()
{
io_服务io_svc;
串口ser_port ser_port(io_svc,这里的字符串);
截止时间计时器超时(io\U svc);
未签名字符my_缓冲区[1];
bool数据_可用=错误;
ser_port.async_read_some(boost::asio::buffer(my_buffer)),
boost::bind(&read_回调)、boost::ref(数据可用)、boost::ref(超时),
boost::asio::占位符::错误,
boost::asio::占位符::字节(已传输);
timeout.expires_from_now(boost::posix_time::millides());
timeout.async_wait(boost::bind(&wait_回调,boost::ref(ser_端口)),
boost::asio::占位符::错误);
io_svc.run();//将阻塞,直到异步回调完成
如果(!数据_可用)
{
启动设备();
}
}
无效读取回调(bool和数据可用,截止时间计时器和超时,常量boost::系统::错误代码和错误,std::大小字节数\u传输)
{
如果(错误| |!字节|已传输)
{
//没有读取任何数据!
数据_可用=错误;
返回;
}
timeout.cancel();//将导致wait_回调触发并出错
可用数据=真;
}
无效等待回调(串行端口和ser端口,常量boost::系统::错误代码和错误)
{
如果(错误)
{
//已读取数据,此超时已取消
返回;
}
ser_port.cancel();//将导致read_回调引发错误
}
这应该让你开始只有一些调整这里和那里,以满足您的具体需求。我希望这有帮助


另一个注意事项:处理回调不需要额外的线程。一切都在调用
run()
的过程中处理。不确定您是否已经意识到了这一点…

这实际上比这里的答案暗示的要简单得多,您可以同步进行:

假设您的阻塞读取是这样的:

size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);
然后你把它换成

socket.non_blocking(true);
size_t len = 0;
error = boost::asio::error::would_block;
while (error == boost::asio::error::would_block)
    //do other things here like go and make coffee
    len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, error);
std::cout.write(recv_buf.data(), len);

您可以使用receive_的另一种重载形式,几乎所有的发送/接收方法都是从这种形式获得的。不幸的是,他们使用了一个flags参数,但0似乎工作得很好。

我使用过这种方法,效果很好,但只是第一次。对io_svc.run()的下一个调用立即返回。我需要做些额外的事情吗?非常感谢,我不得不在io_svc.run()之后调用io_svc.reset()。回答得很好,非常有用。然而,我得到了一些奇怪的行为:我有一个程序每5毫秒向串行端口写入数据,并从中读取另一个数据,但是如果我在读卡器部分将异步延迟设置为5毫秒,我仍然会收到一些与读取值混合的“数据不可用”调用。这是由于代码上的一些开销造成的还是我遗漏了什么?(回复晚了,但既然我在这里,我可能会回答…@joaocandre,如果您的延迟与您的写作频率完全相同,很可能写操作会稍微延迟,读线程的等待时间恰好在两次写尝试之间。从理论上讲,计时器是精确的,但是线程会同步,调度可能会在50%的时间内给您带来不希望的结果。我建议在读取端使用更长的延迟(例如50%的延迟)。您的示例包括套接字示例,而问题是关于串行端口的。