asio:: читать с таймаутом
Мне нужно знать, как читать (синхронизация или асинхронность не имеет значения) с таймаутом. Я хочу проверить, связано ли устройство с последовательным портом или нет.
Для этого я использую asio::write
а потом жду ответа устройства.
Если устройство подключено asio::read(serial, boost::asio::buffer(&r,1))
работает нормально, но если устройства нет, программа останавливается, поэтому мне нужен тайм-аут
Я знаю что мне нужна deadline_timer
но я понятия не имею, как использовать его в async_read
функция.
Пример того, как это работает, был бы очень полезен.
Я знаю, что есть много подобных тем, и я прочитал много из них, но я не могу найти решение, которое поможет мне решить мою проблему!
4 ответа
Код, выложенный Игорем Р., для меня не скомпилирован. Вот моя улучшенная версия его кода, которая отлично работает. Он использует лямбды, чтобы избавиться от set_result
вспомогательная функция.
template <typename SyncReadStream, typename MutableBufferSequence>
void readWithTimeout(SyncReadStream& s, const MutableBufferSequence& buffers, const boost::asio::deadline_timer::duration_type& expiry_time)
{
boost::optional<boost::system::error_code> timer_result;
boost::asio::deadline_timer timer(s.get_io_service());
timer.expires_from_now(expiry_time);
timer.async_wait([&timer_result] (const boost::system::error_code& error) { timer_result.reset(error); });
boost::optional<boost::system::error_code> read_result;
boost::asio::async_read(s, buffers, [&read_result] (const boost::system::error_code& error, size_t) { read_result.reset(error); });
s.get_io_service().reset();
while (s.get_io_service().run_one())
{
if (read_result)
timer.cancel();
else if (timer_result)
s.cancel();
}
if (*read_result)
throw boost::system::system_error(*read_result);
}
Однажды автор библиотеки предложил следующий способ синхронного чтения с тайм-аутом (в этом примере tcp::socket
, но вы можете использовать последовательный порт вместо этого):
void set_result(optional<error_code>* a, error_code b)
{
a->reset(b);
}
template <typename MutableBufferSequence>
void read_with_timeout(tcp::socket& sock,
const MutableBufferSequence& buffers)
{
optional<error_code> timer_result;
deadline_timer timer(sock.io_service());
timer.expires_from_now(seconds(1));
timer.async_wait(boost::bind(set_result, &timer_result, _1));
optional<error_code> read_result;
async_read(sock, buffers,
boost::bind(set_result, &read_result, _1));
sock.io_service().reset();
while (sock.io_service().run_one())
{
if (read_result)
timer.cancel();
else if (timer_result)
sock.cancel();
}
if (*read_result)
throw system_error(*read_result);
}
Вы не используете deadline_timer
в async_read
, Но вы можете запустить два асинхронных процесса:
async_read
процесс по последовательному порту. В boost::asio::serial_port есть метод cancel, который отменяет все асинхронные операции над ним.- Таймер крайнего срока с требуемым таймаутом. В завершении обработчик для
deadline_timer
вы можетеcancel
последовательный порт. Это должно закрытьasync_read
операции и вызовите ее обработчик завершения с ошибкой.
Код:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/array.hpp>
class timed_connection
{
public:
timed_connection( int timeout ) :
timer_( io_service_, boost::posix_time::seconds( timeout ) ),
serial_port_( io_service_ )
{
}
void start()
{
timer_.async_wait
(
boost::bind
(
&timed_connection::stop, this
)
);
// Connect socket
// Write to socket
// async read from serial port
boost::asio::async_read
(
serial_port_, boost::asio::buffer( buffer_ ),
boost::bind
(
&timed_connection::handle_read, this,
boost::asio::placeholders::error
)
);
io_service_.run();
}
private:
void stop()
{
serial_port_.cancel();
}
void handle_read ( const boost::system::error_code& ec)
{
if( ec )
{
// handle error
}
else
{
// do something
}
}
private:
boost::asio::io_service io_service_;
boost::asio::deadline_timer timer_;
boost::asio::serial_port serial_port_;
boost::array< char, 8192 > buffer_;
};
int main()
{
timed_connection conn( 5 );
conn.start();
return 0;
}
Там нет ни одного, ни простого ответа как такового, поскольку даже если вы выполняете асинхронное чтение, обратный вызов никогда не вызывается, и у вас теперь есть свободная нить где-то рядом.
Вы правы, предполагая, что deadline_timer
Это одно из возможных решений, но оно требует некоторого сложного и совместного состояния. Есть пример блокировки TCP, но это для async_connect
и есть крутая вещь, когда он возвращается, когда ему нечего делать. read
в худшем случае этого не случится - произойдет сбой и сгорит из-за неверного ресурса. Итак deadline timer
вещь является одним из ваших вариантов, но на самом деле есть более простой, который выглядит примерно так:
boost::thread *newthread = new boost::thread(boost::bind(&::try_read));
if (!newthread->timed_join(boost::posix_time::seconds(5))) {
newthread->interrupt();
}
По сути, выполните чтение в другом потоке и прекратите его, если истечет время ожидания. Вы должны прочитать о Boost.Threads.
Если вы прервете его, убедитесь, что все ресурсы закрыты.