Подтвердить что ты не робот

Как установить тайм-аут блокировки сокетов в boost asio?

Есть ли способ отменить ожидающую операцию (без отключения) или установить тайм-аут для функций библиотеки boost?

т.е. Я хочу установить тайм-аут блокировки сокета в boost asio?

socket.read_some (boost:: asio:: buffer (pData, maxSize), ошибка _);

Пример: я хочу прочитать некоторые из сокета, но я хочу, чтобы ошибка, если прошло 10 секунд.

4b9b3361

Ответ 1

В Linux/BSD тайм-аут операций ввода-вывода в сокетах напрямую поддерживается операционной системой. Опцию можно включить с помощью setsocktopt(). Я не знаю, предоставляет ли boost::asio метод его установки или предоставляет скрипт сокета, позволяющий вам напрямую установить его - последний случай не является переносимым.

Для полноты здесь описание с man-страницы:

SO_RCVTIMEO и SO_SNDTIMEO

          Specify the receiving or sending  timeouts  until  reporting  an
          error.  The argument is a struct timeval.  If an input or output
          function blocks for this period of time, and data has been  sent
          or  received,  the  return  value  of  that function will be the
          amount of data transferred; if no data has been transferred  and
          the  timeout has been reached then -1 is returned with errno set
          to EAGAIN or EWOULDBLOCK just as if the socket was specified  to
          be  non-blocking.   If  the timeout is set to zero (the default)
          then the operation  will  never  timeout.   Timeouts  only  have
          effect  for system calls that perform socket I/O (e.g., read(2),
          recvmsg(2), send(2), sendmsg(2)); timeouts have  no  effect  for
          select(2), poll(2), epoll_wait(2), etc.

Ответ 2

Когда этот вопрос был задан, я полагаю, что у ASIO не было никакого примера о том, как выполнить то, что требуется OP, то есть перерыв в операции блокировки, такой как операция блокирующего сокета. Теперь есть примеры, чтобы показать вам, как это сделать. пример кажется длинным, но это потому, что это WELL прокомментировал. Он показывает, как использовать ioservice в режиме "одного снимка".

Я думаю, что этот пример - отличное решение. Другие решения здесь нарушают переносимость и не используют ioservice. если переносимость не важна, и ioservice кажется много накладными - ДЕЙСТВИЕ - вы не должны использовать ASIO. Независимо от того, у вас будет создана ioservice (от нее зависит практически все функциональные возможности ASIO, даже синхронизированные сокеты), поэтому воспользуйтесь ею.

Тайм-аут блокировки asio tcp

Тайм-аут блокировки asio udp

Документация по ASIO обновлена, поэтому ознакомьтесь с новыми примерами о том, как преодолеть некоторые из возможностей использования ASI "gotchas".

Ответ 3

Вы можете сделать async_read, а также установить таймер для нужного времени. Затем, если таймер срабатывает, отмените вызов на свой объект сокета. В противном случае, если ваше чтение происходит, вы можете отменить свой таймер. Это требует, чтобы вы, конечно, использовали объект io_service.

edit: Найден фрагмент кода для вас, который делает это

http://lists.boost.org/Archives/boost/2007/04/120339.php

Ответ 4

У меня был тот же вопрос, и после некоторых исследований самое простое, самое чистое решение, которое я мог придумать, состояло в том, чтобы получить основной родной сокет и сделать выбор до тех пор, пока не будут данные для чтения. Select будет принимать параметр таймаута. Конечно, работа с родным сокетом начинает идти против того, чтобы использовать asio в первую очередь, но опять же, это кажется самым чистым способом. Насколько я мог судить, asio не дает возможности сделать это для синхронного использования. Код:

        // socket here is:  boost::shared_ptr<boost::asio::ip::tcp::socket> a_socket_ptr

        // Set up a timed select call, so we can handle timeout cases.

        fd_set fileDescriptorSet;
        struct timeval timeStruct;

        // set the timeout to 30 seconds
        timeStruct.tv_sec = 30;
        timeStruct.tv_usec = 0;
        FD_ZERO(&fileDescriptorSet);

        // We'll need to get the underlying native socket for this select call, in order
        // to add a simple timeout on the read:

        int nativeSocket = a_socket_ptr->native();

        FD_SET(nativeSocket,&fileDescriptorSet);

        select(nativeSocket+1,&fileDescriptorSet,NULL,NULL,&timeStruct);

        if(!FD_ISSET(nativeSocket,&fileDescriptorSet)){ // timeout

                std::string sMsg("TIMEOUT on read client data. Client IP: ");

                sMsg.append(a_socket_ptr->remote_endpoint().address().to_string());

                throw MyException(sMsg);
        }

        // now we know there something to read, so read
        boost::system::error_code error;
        size_t iBytesRead = a_socket_ptr->read_some(boost::asio::buffer(myVector), error);

        ...

Возможно, это будет полезно для вашей ситуации.

Ответ 5

TL; DR

socket.set_option(boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO>{ 200 });

ПОЛНЫЙ ОТВЕТ Этот вопрос постоянно задают снова и снова на протяжении многих лет. Ответы, которые я видел до сих пор, довольно скудны. Я добавлю эту информацию прямо здесь, в одном из первых появлений этого вопроса.

Каждый, кто пытается использовать ASIO для упрощения своего сетевого кода, был бы очень рад, если бы автор просто добавил необязательный параметр timeout для всех функций синхронизации и асинхронности. К сожалению, это вряд ли произойдет (по моему скромному мнению, просто по идеологическим причинам, в конце концов, AS в ASIO есть причина).

Так что эти способы избавиться от этой бедной кошки, доступные до сих пор, ни один из них не является особенно аппетитным. Допустим, нам нужно время ожидания 200 мс.

1) Хороший (плохой) старый API сокетов:

const int timeout = 200;
::setsockopt(socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof timeout);//SO_SNDTIMEO for send ops

Обратите внимание на эти особенности: - const int для тайм-аута - в Windows требуемый тип на самом деле является DWORD, но, к счастью, в текущем наборе компиляторов он одинаков, поэтому const int будет работать как в Win, так и в мире Posix. - (const char *) для значения. В Windows требуется const char *, Posix требует const void *, в C++ const char * автоматически преобразуется в const void *, в то время как обратное неверно.

Преимущества: работает и, вероятно, всегда будет работать, поскольку API сокетов старый и стабильный. Достаточно просто. Быстро. Недостатки: технически могут потребоваться соответствующие заголовочные файлы (разные в Win и даже разные версии UNIX) для setsockopt и макросов, но текущая реализация ASIO в любом случае загрязняет глобальное пространство имен. Требуется переменная для тайм-аута. Не типобезопасно. В Windows требует, чтобы сокет работал в режиме перекрытия (который, к счастью, использует текущая реализация ASIO, но это все еще деталь реализации). Некрасиво!

2) Пользовательский вариант сокета ASIO:

typedef boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO> rcv_timeout_option; //somewhere in your headers to be used everywhere you need it
//...
socket.set_option(rcv_timeout_option{ 200 });

Преимущества: достаточно просто. Быстро. Красивый (с typedef). Недостатки: Зависит от деталей реализации ASIO, которые могут измениться (но OTOH все изменится со временем, и такие детали будут меняться реже, чем общедоступные API, подлежащие стандартизации). Но в случае, если это произойдет, вам придется либо написать класс в соответствии с https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/SettableSocketOption.html (который, конечно, основной PITA благодаря очевидному переобработке этой части ASIO) или еще лучше вернуться к 1.

3) Используйте C++ асинхронные/будущие объекты.

#include <future>
#include <chrono>
//...
auto status = std::async(std::launch::async, [&] (){ /*your stream ops*/ })
    .wait_for(std::chrono::milliseconds{ 200 });
switch (status)
    {
    case std::future_status::deferred:
    //... should never happen with std::launch::async
        break;
    case std::future_status::ready:
    //...
        break;
    case std::future_status::timeout:
    //...
        break;
    }

Преимущества: стандарт. Недостатки: всегда запускает новый поток (на практике), который является относительно медленным (может быть достаточно для клиентов, но приведет к уязвимости DoS для серверов, поскольку потоки и сокеты являются "дорогими" ресурсами). Не пытайтесь использовать std :: launch :: deferred вместо std :: launch :: async, чтобы избежать запуска нового потока, так как wait_for всегда будет возвращать future_status :: deferred, не пытаясь запустить код.

4) Метод, предписанный ASIO - использовать только асинхронные операции (что на самом деле не является ответом на вопрос).

Преимущества: достаточно хорошо для серверов, если не требуется огромная масштабируемость для коротких транзакций. Недостатки: довольно многословно (поэтому я даже не буду приводить примеры - см. Примеры ASIO). Требует очень тщательного управления временем жизни всех ваших объектов, используемых как асинхронными операциями, так и их обработчиками завершения, что на практике требует, чтобы все классы, содержащие и использующие такие данные в асинхронных операциях, были получены из enable_shared_from_this, что требует, чтобы все такие классы выделялись в куче, что означает ( по крайней мере, для коротких операций) эта масштабируемость начнет уменьшаться примерно после 16 потоков, так как каждая куча alloc/dealloc будет использовать барьер памяти.

Ответ 6

Следуя тому, о чем упомянул grepsedawk. Существует несколько примеров, показывающих, как отменить длительные асинхронные операции по истечении определенного периода времени в разделе Тайм-ауты внутри asio doco. Примеры Boost Asio , Клиент Async TCP помог мне больше всего.

Счастливый Asyncing:)

Ответ 7

Даже спустя годы после первоначального вопроса, до сих пор нет удовлетворительного ответа.

Использование выбора вручную не является хорошим вариантом

  1. номер дескриптора файла должен быть меньше 1024
  2. Из-за неправильной контрольной суммы FD может показаться готовым.

Вызов io_service.run_one() также является плохой идеей, поскольку могут существовать другие асинхронные параметры, для которых всегда требуется io_service для run(). И ускорить документ о блокировке tcp клиента сложно понять.

Так вот мое решение. Основная идея заключается в следующем:

{
    Semaphore r_sem;
    boost::system::error_code r_ec;
    boost::asio::async_read(s,buffer,
                            [this, &r_ec, &r_sem](const boost::system::error_code& ec_, size_t) {
                                r_ec=ec_;
                                r_sem.notify();
                            });
    if(!r_sem.wait_for(std::chrono::seconds(3))) // wait for 3 seconds
    {
        s.cancel();
        r_sem.wait();
        throw boost::system::system_error(boost::asio::error::try_again);
    }
    else if(r_ec)
        throw boost::system::system_error(r_ec);
}

Здесь Semaphore - это просто мьютекс и переменная условия.
wait_for реализуется http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for

Полный код находится по адресу https://github.com/scinart/cpplib/blob/master/include/asio.hpp.
Примеры есть в https://github.com/scinart/cpplib/blob/master/test/test_asio.cpp
Лучший пример на https://github.com/scinart/cpplib/blob/master/test/test_SyncBoostIO.cpp

Ответ 8

Вы можете обернуть синхронные вызовы в фьючерсы и дождаться завершения его с помощью тайм-аута (wait_timeout).

http://www.boost.org/doc/libs/1_47_0/doc/html/thread/synchronization.html#thread.synchronization.futures

Конечно, ни один размер не подходит для всех, но работает хорошо, например. обходя медленные таймауты подключения.

Ответ 9

В * nix вы будете использовать alarm(), чтобы ваш сотовый вызов завершился с EINTR