Как выполнить неблокирующее чтение с помощью asio? - PullRequest
12 голосов
/ 19 января 2009

Я пытаюсь использовать boost :: asio для чтения и записи с устройства через последовательный порт. Оба блока boost :: asio: read () и boost :: asio :: serial_port :: read_some (), когда читать нечего. Вместо этого я хотел бы обнаружить это условие и записать команду в порт для запуска устройства.

Как я могу обнаружить, что данные недоступны?

При необходимости я могу делать все асинхронно, я бы просто предпочел избежать дополнительной сложности, если смогу.

Ответы [ 3 ]

17 голосов
/ 17 ноября 2009

У вас есть пара вариантов, на самом деле. Вы можете использовать встроенную функцию последовательного порта async_read_some или автономную функцию boost::asio::async_read (или async_read_some).

Вы все равно столкнетесь с ситуацией, когда вы фактически «заблокированы», поскольку ни один из них не вызовет обратный вызов, если (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 голосов
/ 06 января 2017

Это на самом деле намного проще, чем подразумевали ответы, и вы можете сделать это синхронно:

Предположим, что ваше блокирующее чтение было примерно таким:

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, которую имеют почти все методы send / receive. К сожалению, они принимают аргумент flags, но 0, кажется, работает нормально.

1 голос
/ 16 апреля 2009

Вы должны использовать свободную функцию asio :: async_read.

...