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

时间:2009-01-18 22:32:02

标签: boost serial-port boost-asio

我试图使用boost :: asio来读取和写入串口设备。当没有任何东西要读时,boost :: asio:read()和boost :: asio :: serial_port :: read_some()都会阻塞。相反,我想检测这种情况并向端口写一个命令以启动设备。

如何检测到没有可用数据?

如果有必要,我可以异步完成所有事情,如果可以的话,我宁愿避免额外的复杂性。

3 个答案:

答案 0 :(得分:17)

实际上你有几个选择。您可以使用串口的内置async_read_some功能,也可以使用独立功能boost::asio::async_read(或async_read_some)。

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

增加的复杂性并非真的那么糟糕。最终会有两个具有类似行为的回调。如果“读取”或“超时”回调触发错误,您就会知道它是失败者。如果其中任何一个没有发生错误,那么你知道它是比赛获胜者(你应该取消另一个电话)。在您阻止拨打read_some的地方,您现在可以拨打io_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
}

这应该让你开始只需要一些调整就可以满足你的特定需求。我希望这有帮助!

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

答案 1 :(得分:2)

它实际上比这里暗示的答案简单得多,你可以同步地做到这一点:

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

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_from的替代重载形式。不幸的是他们拿了一个标志参数但是0似乎工作正常。

答案 2 :(得分:1)

你必须使用自由函数asio :: async_read。