Asio:: чтение с тайм-аутом
Мне нужно знать, как читать (синхронизация или асинхронность не имеет значения) с таймаутом. Я хочу проверить, связано ли устройство с последовательным портом или нет.
Для этого я использую asio::write
, а затем жду ответа на устройство.
Если устройство подключено asio::read(serial, boost::asio::buffer(&r,1))
работает нормально, но если нет устройства, программа останавливается, и именно поэтому мне нужен тайм-аут
Я знаю, что мне нужен deadline_timer
, но я не знаю, как использовать его в функции async_read
.
Пример того, как это работает, будет действительно полезным.
Я знаю, что существует много похожих потоков, и я много читаю, но не могу найти решение, помогающее мне решить мою проблему.
Ответы
Ответ 1
Код отправленный Игорем Р., не был скомпилирован для меня. Вот моя улучшенная версия его кода, которая отлично работает. Он использует lambdas, чтобы избавиться от вспомогательной функции 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);
}
Ответ 2
Вы не используете deadline_timer
в async_read
. Но вы можете запустить два асинхронных процесса:
- Процесс
async_read
на последовательном порту. boost:: asio:: serial_port имеет cancel метод, который отменяет все операции async на нем.
- Таймер с заданным тайм-аутом. В обработчике завершения для
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;
}
Ответ 3
Когда-то автор библиотеки предложил следующий способ читать синхронно с таймаутом (этот пример включает 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);
}
Ответ 4
Нет никакого или простого ответа как такового, так как даже если вы читаете асинхронное чтение, обратный вызов никогда не вызывается, и теперь у вас есть свободный поток где-то рядом.
Вы правы, полагая, что 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.
Если вы прервите его, убедитесь, что все ресурсы закрыты.
Ответ 5
Простое решение заключается в использовании "io_context_.run_for()". Вот код,
#include "asio/buffer.hpp"
#include "asio/connect.hpp"
#include "asio/io_context.hpp"
#include "asio/ip/tcp.hpp"
#include "asio/read_until.hpp"
#include "asio/system_error.hpp"
#include "asio/write.hpp"
#include <cstdlib>
#include <iostream>
#include <string>
using asio::ip::tcp;
//----------------------------------------------------------------------
//
// This class manages socket timeouts by running the io_context using the timed
// io_context::run_for() member function. Each asynchronous operation is given
// a timeout within which it must complete. The socket operations themselves
// use lambdas as completion handlers. For a given socket operation, the client
// object runs the io_context to block thread execution until the operation
// completes or the timeout is reached. If the io_context::run_for() function
// times out, the socket is closed and the outstanding asynchronous operation
// is cancelled.
//
class client
{
public:
void connect(const std::string& host, const std::string& service,
std::chrono::steady_clock::duration timeout)
{
// Resolve the host name and service to a list of endpoints.
auto endpoints = tcp::resolver(io_context_).resolve(host, service);
// Start the asynchronous operation itself. The lambda that is used as a
// callback will update the error variable when the operation completes.
// The blocking_udp_client.cpp example shows how you can use std::bind
// rather than a lambda.
std::error_code error;
asio::async_connect(socket_, endpoints,
[&](const std::error_code& result_error,
const tcp::endpoint& /*result_endpoint*/)
{
error = result_error;
});
// Run the operation until it completes, or until the timeout.
run(timeout);
// Determine whether a connection was successfully established.
if (error)
throw std::system_error(error);
}
std::string read_line(std::chrono::steady_clock::duration timeout)
{
// Start the asynchronous operation. The lambda that is used as a callback
// will update the error and n variables when the operation completes. The
// blocking_udp_client.cpp example shows how you can use std::bind rather
// than a lambda.
std::error_code error;
std::size_t n = 0;
asio::async_read_until(socket_,
asio::dynamic_buffer(input_buffer_), '\n',
[&](const std::error_code& result_error,
std::size_t result_n)
{
error = result_error;
n = result_n;
});
// Run the operation until it completes, or until the timeout.
run(timeout);
// Determine whether the read completed successfully.
if (error)
throw std::system_error(error);
std::string line(input_buffer_.substr(0, n - 1));
input_buffer_.erase(0, n);
return line;
}
void write_line(const std::string& line,
std::chrono::steady_clock::duration timeout)
{
std::string data = line + "\n";
// Start the asynchronous operation itself. The lambda that is used as a
// callback will update the error variable when the operation completes.
// The blocking_udp_client.cpp example shows how you can use std::bind
// rather than a lambda.
std::error_code error;
asio::async_write(socket_, asio::buffer(data),
[&](const std::error_code& result_error,
std::size_t /*result_n*/)
{
error = result_error;
});
// Run the operation until it completes, or until the timeout.
run(timeout);
// Determine whether the read completed successfully.
if (error)
throw std::system_error(error);
}
private:
void run(std::chrono::steady_clock::duration timeout)
{
// Restart the io_context, as it may have been left in the "stopped" state
// by a previous operation.
io_context_.restart();
// Block until the asynchronous operation has completed, or timed out. If
// the pending asynchronous operation is a composed operation, the deadline
// applies to the entire operation, rather than individual operations on
// the socket.
io_context_.run_for(timeout);
// If the asynchronous operation completed successfully then the io_context
// would have been stopped due to running out of work. If it was not
// stopped, then the io_context::run_for call must have timed out.
if (!io_context_.stopped())
{
// Close the socket to cancel the outstanding asynchronous operation.
socket_.close();
// Run the io_context again until the operation completes.
io_context_.run();
}
}
asio::io_context io_context_;
tcp::socket socket_{io_context_};
std::string input_buffer_;
};
//----------------------------------------------------------------------
int main(int argc, char* argv[])
{
try
{
if (argc != 4)
{
std::cerr << "Usage: blocking_tcp_client <host> <port> <message>\n";
return 1;
}
client c;
c.connect(argv[1], argv[2], std::chrono::seconds(10));
auto time_sent = std::chrono::steady_clock::now();
c.write_line(argv[3], std::chrono::seconds(10));
for (;;)
{
std::string line = c.read_line(std::chrono::seconds(10));
// Keep going until we get back the line that was sent.
if (line == argv[3])
break;
}
auto time_received = std::chrono::steady_clock::now();
std::cout << "Round trip time: ";
std::cout << std::chrono::duration_cast<
std::chrono::microseconds>(
time_received - time_sent).count();
std::cout << " microseconds\n";
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}