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

发布于 2024-07-12 05:42:33 字数 223 浏览 11 评论 0原文

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

我怎样才能检测到没有可用数据?

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

I am attempting to use boost::asio to read and write from a device on a serial port. Both boost::asio:read() and boost::asio::serial_port::read_some() block when there is nothing to read. Instead I would like to detect this condition and write a command to the port to kick-start the device.

How can I either detect that no data is available?

If necessary I can do everything asynchronously, I would just rather avoid the extra complexity if I can.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

凯凯我们等你回来 2024-07-19 05:42:33

实际上,你有几个选择。 您可以使用串行端口的内置 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() 中处理。 不确定你是否已经意识到这一点......

You have a couple of options, actually. You can either use the serial port's built-in async_read_some function, or you can use the stand-alone function boost::asio::async_read (or async_read_some).

You'll still run into the situation where you are effectively "blocked", since neither of these will call the callback unless (1) data has been read or (2) an error occurs. To get around this, you'll want to use a deadline_timer object to set a timeout. If the timeout fires first, no data was available. Otherwise, you will have read data.

The added complexity isn't really all that bad. You'll end up with two callbacks with similar behavior. If either the "read" or the "timeout" callback fires with an error, you know it's the race loser. If either one fires without an error, then you know it's the race winner (and you should cancel the other call). In the place where you would have had your blocking call to read_some, you will now have a call to io_svc.run(). Your function will still block as before when it calls run, but this time you control the duration.

Here's an example:

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
}

That should get you started with only a few tweaks here and there to suit your specific needs. I hope this helps!

Another note: No extra threads were necessary to handle callbacks. Everything is handled within the call to run(). Not sure if you were already aware of this...

小忆控 2024-07-19 05:42:33

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

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

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 的替代重载形式,几乎所有发送/接收方法都具有。 不幸的是,他们采用了 flags 参数,但 0 似乎工作正常。

Its actually a lot simpler than the answers here have implied, and you can do it synchronously:

Suppose your blocking read was something like this:

size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);

Then you replace it with

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);

You use the alternative overloaded form of receive_from which almost all the send/receive methods have. They unfortunately take a flags argument but 0 seems to work fine.

二货你真萌 2024-07-19 05:42:33

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

You have to use the free-function asio::async_read.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文