代码之家  ›  专栏  ›  技术社区  ›  Joe Ludwig

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

  •  12
  • Joe Ludwig  · 技术社区  · 17 年前

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

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

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

    3 回复  |  直到 9 年前
        1
  •  17
  •   Brian    16 年前

    实际上,你有几个选择。您可以使用串行端口的内置 async_read_some 函数,或者可以使用独立函数 boost::asio::async_read (或) 异步读写器 )

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

    增加的复杂性并没有那么糟糕。你最终会得到两个行为相似的回调。如果“read”或“timeout”回调因错误而触发,您就知道这是比赛输家。如果其中一个没有错误地开火,那么你就知道这是比赛的胜利者(你应该取消另一个呼叫)。在你本可以接到阻止电话的地方 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() . 不确定你是否已经意识到这一点…

        2
  •  2
  •   Motorhead    9 年前

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

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

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

    您可以使用几乎所有发送/接收方法都使用的另一种重载形式的接收。不幸的是,它们采用了标志参数,但0似乎工作正常。

        3
  •  1
  •   Nicolas    17 年前

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