Как выполнить неблокирующее чтение с помощью asio?

StackOverflow https://stackoverflow.com/questions/456042

  •  19-08-2019
  •  | 
  •  

Вопрос

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

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

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

Это было полезно?

Решение

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

Вы все равно столкнетесь с ситуацией, когда вы фактически & заблокированы " поскольку ни один из них не вызовет обратный вызов, если (1) данные не были прочитаны или (2) произошла ошибка , Чтобы обойти это, вы можете использовать объект read_some для установки времени ожидания. Если тайм-аут срабатывает первым, данные недоступны. В противном случае вы будете читать данные.

Сложность не так уж и плоха. Вы получите два обратных вызова с похожим поведением. Если либо & Quot; прочитайте & Quot; или " timeout " обратный вызов срабатывает с ошибкой, вы знаете, что это проигравший гонки. Если один из них стреляет без ошибки, то вы знаете, что он победитель гонки (и вам следует отменить другой колл). В том месте, где у вас был бы блокирующий звонок на io_svc.run(), теперь вы будете звонить на 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
}

Это должно помочь вам начать с нескольких настроек здесь и там, чтобы удовлетворить ваши конкретные потребности. Надеюсь, это поможет!

Еще одно примечание: для обработки обратных вызовов не требуется никаких дополнительных потоков. Все обрабатывается в вызове на <=>. Не уверен, что вы уже знали об этом ...

Другие советы

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

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

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, кажется, работает нормально.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top