Так RCVTIME и так RCVTIMEO не влияет на повышение.Операции Asio

ниже мой код

boost::asio::io_service io;
boost::asio::ip::tcp::acceptor::reuse_address option(true);
boost::asio::ip::tcp::acceptor accept(io);
boost::asio::ip::tcp::resolver resolver(io);
boost::asio::ip::tcp::resolver::query query("0.0.0.0", "8080");
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
accept.open(endpoint.protocol());
accept.set_option(option);
accept.bind(endpoint);
accept.listen(30);

boost::asio::ip::tcp::socket ps(io);

accept.accept(ps);

struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
//setsockopt(ps.native(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
setsockopt(ps.native(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
char buf[1024];
ps.async_receive(boost::asio::buffer(buf, 1024), boost::bind(fun));
io.run();

когда я использую Telnet для подключения, но не отправляю данные, он не отключается от таймаута Telnet.Нужно будет сделать, чтобы setsockopt пнул? Спасибо!

Я изменил SO_RCVTIMEO на SO_SNDTIMEO. По-прежнему не удается тайм-аут в указанное время

2 ответов


используя SO_RCVTIMEO и SO_SNDTIMEO параметры сокета с Boost.Asio редко будет производить желаемое поведение. Рассмотрите возможность использования одного из следующих двух моделей:

Составленная Операция С async_wait()

можно составить асинхронную операцию чтения с таймаутом с помощью Boost.Таймер асио и async_wait() работы с async_receive() операции. Этот подход продемонстрирован в Boost.Asio примеры тайм-аута, что-то подобное кому:

// Start a timeout for the read.
boost::asio::deadline_timer timer(io_service);
timer.expires_from_now(boost::posix_time::seconds(1));
timer.async_wait(
  [&socket, &timer](const boost::system::error_code& error)
  {
    // On error, such as cancellation, return early.
    if (error) return;

    // Timer has expired, but the read operation's completion handler
    // may have already ran, setting expiration to be in the future.
    if (timer.expires_at() > boost::asio::deadline_timer::traits_type::now())
    {
      return;
    } 

    // The read operation's completion handler has not ran.
    boost::system::error_code ignored_ec;
    socket.close(ignored_ec);
  });

// Start the read operation.
socket.async_receive(buffer,
  [&socket, &timer](const boost::system::error_code& error,
    std::size_t bytes_transferred)
  {
    // Update timeout state to indicate the handler has ran.  This
    // will cancel any pending timeouts.
    timer.expires_at(boost::posix_time::pos_infin);

    // On error, such as cancellation, return early.
    if (error) return;

    // At this point, the read was successful and buffer is populated.
    // However, if the timeout occurred and its completion handler ran first,
    // then the socket is closed (!socket.is_open()).
  });

имейте в виду, что обе асинхронные операции могут выполняться в одной итерации, что делает оба обработчика завершения готовыми к успешному запуску. Следовательно, причина, по которой обоим обработчикам завершения необходимо обновить и проверить состояние. См.этой ответ для получения более подробной информации о том, как управлять состоянием.

использовать std::future

импульс.Asio обеспечивает поддержка фьючерсов на C++11. Когда boost::asio::use_future предоставленный в качестве обработчика завершения асинхронной операции, инициирующая функция возвращает std::future это будет выполнено после завершения операции. As std::future поддерживает временные ожидания, можно использовать его для тайм-аута операции. Обратите внимание, что поскольку вызывающий поток будет заблокирован в ожидании будущего, по крайней мере один другой поток должен обрабатывать io_service разрешить async_receive() операция для прогресса и выполнения обещания:

// Use an asynchronous operation so that it can be cancelled on timeout.
std::future<std::size_t> read_result = socket.async_receive(
   buffer, boost::asio::use_future);

// If timeout occurs, then cancel the read operation.
if (read_result.wait_for(std::chrono::seconds(1)) == 
    std::future_status::timeout)
{
  socket.cancel();
}
// Otherwise, the operation completed (with success or error).
else
{
  // If the operation failed, then read_result.get() will throw a
  // boost::system::system_error.
  auto bytes_transferred = read_result.get();
  // process buffer
}

почему SO_RCVTIMEO Не Работает

Поведение Системы

на SO_RCVTIMEO документация отмечает, что этот параметр влияет только на системные вызовы, выполняющие ввод-вывод сокета, такие как read() и recvmsg(). Это не влияет на демультиплексоры событий, такие как select() и poll(), что только смотреть дескрипторы файлов, чтобы определить, когда ввод / вывод может произойти без блокировки. Кроме того, когда тайм-аут происходит, вызов ввода/вывода не возвращение -1 и телеаппаратуры errno до EAGAIN или EWOULDBLOCK.

укажите таймауты приема или отправки до сообщения об ошибке. [...] если данные не были переданы и тайм-аут был достигнут, то -1 возвращается с errno равным EAGAIN или EWOULDBLOCK [...] Тайм-ауты действуют только для системных вызовов, выполняющих ввод/вывод сокета (например,read(), recvmsg(), [...]; тайм-ауты не влияют на select(), poll(), epoll_wait() и так на.

когда базовый дескриптор файла установлен в неблокирующий, системные вызовы, выполняющие ввод-вывод сокета, немедленно вернутся с EAGAIN или EWOULDBLOCK если ресурсы не доступны сразу. Для неблокирующего сокета, SO_RCVTIMEO не будет иметь никакого влияния, так как вызов вернется немедленно с успехом или неудачей. Таким образом, для SO_RCVTIMEO чтобы повлиять на системные вызовы ввода-вывода, сокет должен быть заблокирован.

импульс.Асио Поведение

первый, асинхронные операции ввода-вывода в Boost.Asio будет использовать демультиплексор событий, например select() или poll(). Следовательно,SO_RCVTIMEO не влияет на асинхронные операции.

Далее, Импульс.Сокеты Asio имеют концепцию двух неблокирующих режимов (оба из которых по умолчанию false):

  • native_non_blocking() режим, который примерно соответствует неблокирующему состоянию файлового дескриптора. Этот режим влияет на системные вызовы ввода-вывода. Например, если один вызывает socket.native_non_blocking(true), потом recv(socket.native_handle(), ...) может потерпеть неудачу с errno значение EAGAIN или EWOULDBLOCK. В любое время асинхронная операция инициируется на сокете, Boost.Asio включит этот режим.
  • non_blocking() режим, который влияет на повышение.Деятельность гнезда Asio одновременная. Когда установлено значение true, импульс.Asio установит базовый файловый дескриптор как неблокирующий и синхронный Boost.Операции сокета Asio могут завершиться с boost::asio::error::would_block (или эквивалент системная ошибка). При установке false, импульс.Asio блокирует, даже если базовый файловый дескриптор не блокируется, путем опроса файлового дескриптора и повторной попытки системных операций ввода-вывода, если EAGAIN или EWOULDBLOCK возвращаются.

поведение non_blocking() предупреждает SO_RCVTIMEO от создания желаемого поведения. Предполагая socket.receive() вызывается и данные не доступны и не получил:

  • если non_blocking() является false, системный вызов ввода-вывода будет тайм-аут на SO_RCVTIMEO. Однако, Повышение.Затем Asio немедленно заблокирует опрос в файловом дескрипторе для чтения, на который не влияет SO_RCVTIMEO. Конечный результат-вызывающий заблокирован в socket.receive() пока данные были получены или провала, такие как удаленный участник закрытия подключения.
  • если non_blocking() имеет значение true, тогда базовый файловый дескриптор также не блокируется. Следовательно, Вызов системного ввода-вывода будет игнорировать SO_RCVTIMEO, тут же вернется с EAGAIN или EWOULDBLOCK, причинив socket.receive() сбой с boost::asio::error::would_block.

в идеале, для SO_RCVTIMEO для работы с Boost.Asio, нужно native_non_blocking() установите значение false, чтобы SO_RCVTIMEO смогите принять аффект, но также имейте non_blocking() установите значение true, чтобы предотвратить опрос дескриптора. Тем Не Менее, Boost.Asio не поддержка этой:

socket::native_non_blocking(bool mode)

если режим false, но текущее значение non_blocking() is true, эта функция не работает с boost::asio::error::invalid_argument, как комбинация не имеет смысла.


так как вы получаете данные, можно установить: SO_RCVTIMEO вместо SO_SNDTIMEO

хотя смешивание boost и системных вызовов может не дать ожидаемых результатов.

Для справки:

SO_RCVTIMEO

задает значение тайм-аута, указывающее максимальное время ожидания функции ввода до ее завершения. Он принимает время структура с количеством секунд и микросекунд, определяющих ограничить время ожидания завершения операции ввода. Если операция receive заблокирована на это время без получения дополнительные данные, оно возвратит с частично отсчетом или errno установленным к [EAGAIN] или [EWOULDBLOCK] если данные не получены. По умолчанию для этого опция равна нулю, что означает, что операция получения не должна время. Этот параметр принимает структуру timeval. Обратите внимание, что не все реализации позволяют установить этот параметр.

этот опция, однако, влияет только на операции чтения, а не на другую функцию низкого уровня, которая может ждать сокета в асинхронной реализации (например, select и epoll), и кажется, что она также не влияет на асинхронные операции asio.

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

упрощенный пример (будет скомпилирован на c++11):

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>

void myclose(boost::asio::ip::tcp::socket& ps) { ps.close(); }

int main()
{
  boost::asio::io_service io;
  boost::asio::ip::tcp::acceptor::reuse_address option(true);
  boost::asio::ip::tcp::acceptor accept(io);
  boost::asio::ip::tcp::resolver resolver(io);
  boost::asio::ip::tcp::resolver::query query("0.0.0.0", "8080");
  boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
  accept.open(endpoint.protocol());
  accept.set_option(option);
  accept.bind(endpoint);
  accept.listen(30);
  boost::asio::ip::tcp::socket ps(io);
  accept.accept(ps);
  char buf[1024];
  boost::asio::deadline_timer timer(io, boost::posix_time::seconds(1));
  timer.async_wait(boost::bind(myclose, boost::ref(ps))); 
  ps.async_receive(boost::asio::buffer(buf, 1024),
           [](const boost::system::error_code& error,
              std::size_t bytes_transferred )
           {
             std::cout << bytes_transferred << std::endl;
           });
  io.run();
  return 0;
}