Pregunta

¿Hay alguna forma de cancelar una operación pendiente (sin desconectar) o establecer un tiempo de espera para las funciones de la biblioteca de impulso?

I.e. ¿Quiero establecer un tiempo de espera para bloquear el socket en boost asio?

socket.read_some (boost :: asio :: buffer (pData, maxSize), error_);

Ejemplo: quiero leer algo del socket, pero quiero arrojar un error si han pasado 10 segundos.

¿Fue útil?

Solución

En Linux / BSD, el sistema operativo admite directamente el tiempo de espera en operaciones de E / S en sockets. La opción se puede habilitar a través de setsocktopt () . No sé si boost :: asio proporciona un método para configurarlo o expone el scriptor de socket para permitirle configurarlo directamente; este último caso no es realmente portátil.

Para completar, aquí está la descripción de la página del manual:

  

SO_RCVTIMEO y 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.

Otros consejos

Cuando se hizo esta pregunta, supongo que ASIO no tenía ningún ejemplo sobre cómo lograr lo que el OP necesitaba, es decir, agotar el tiempo de espera de una operación de bloqueo, como una operación de socket de bloqueo. Ahora existen ejemplos para mostrarle exactamente cómo hacer esto. el ejemplo parece largo, pero eso es porque está BIEN comentado. Muestra cómo usar el ioservicio en un tipo de modo 'one shot'.

Creo que el ejemplo es una gran solución. Las otras soluciones aquí rompen la portabilidad y no aprovechan el ioservicio. si la portabilidad no es importante y el ioservicio parece tener demasiada sobrecarga, ENTONCES, no debería usar ASIO. No importa qué, tendrá un ioservicio creado (casi toda la funcionalidad ASIO depende de él, incluso sincronizar sockets), así que aproveche.

Tiempo de espera de una operación de bloqueo asio tcp

Tiempo de espera de una operación asio udp de bloqueo

La documentación de ASIO se ha actualizado, así que échale un vistazo para ver nuevos ejemplos sobre cómo superar algunas de las 'trampas' que solía tener ASIO.

Puede hacer un async_read y también configurar un temporizador para el tiempo de espera deseado. Luego, si se dispara el temporizador, llame a cancel en su objeto de socket. De lo contrario, si su lectura ocurre, puede cancelar su temporizador. Esto requiere que utilice un objeto io_service, por supuesto.

editar: Encontré un fragmento de código para usted que hace esto

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

Tenía la misma pregunta, y después de investigar un poco, la solución más simple y limpia que se me ocurrió fue obtener el socket nativo subyacente y hacer una selección hasta que hubiera datos para leer. Seleccionar tomará un parámetro de tiempo de espera. Por supuesto, trabajar con el socket nativo comienza a ir en contra del uso de asio en primer lugar, pero nuevamente, esta parece ser la forma más limpia. Por lo que pude ver, asio no proporciona una manera de hacer esto para el uso sincrónico fácilmente. Código:

        // 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's something to read, so read
        boost::system::error_code error;
        size_t iBytesRead = a_socket_ptr->read_some(boost::asio::buffer(myVector), error);

        ...

Quizás esto sea útil para su situación.

TL; DR

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

RESPUESTA COMPLETA Esta pregunta se sigue haciendo una y otra vez durante muchos años. Las respuestas que vi hasta ahora son bastante pobres. Agregaré esta información aquí en uno de los primeros casos de esta pregunta.

Todos los que intenten usar ASIO para simplificar su código de red estarán perfectamente contentos si el autor simplemente agrega un tiempo de espera de parámetro opcional a todas las funciones de sincronización y async io. Desafortunadamente, es poco probable que esto suceda (en mi humilde opinión, solo por razones ideológicas, después de todo, AS en ASIO es por una razón).

Así que estas son las formas de desollar este pobre gato disponible hasta ahora, ninguno de ellos especialmente apetitoso. Digamos que necesitamos 200 ms de tiempo de espera.

1) API de socket antigua buena (mala):

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

Tenga en cuenta esas peculiaridades: - const int for timeout: en Windows, el tipo requerido es en realidad DWORD, pero afortunadamente el conjunto actual de compiladores tiene el mismo, por lo que const int funcionará tanto en Win como en Posix. - (const char *) para el valor. En Windows se requiere const char *, Posix requiere const void *, en C ++ const char * se convertirá en const void * en silencio mientras que lo contrario no es cierto.

Ventajas: funciona y probablemente siempre funcionará, ya que la API de socket es antigua y estable. Suficientemente simple. Rápido. Desventajas: técnicamente puede requerir archivos de encabezado apropiados (diferentes en Win e incluso diferentes versiones de UNIX) para setsockopt y las macros, pero la implementación actual de ASIO contamina el espacio de nombres global con ellos de todos modos. Requiere una variable para el tiempo de espera. No es de tipo seguro. En Windows, requiere que el socket esté en modo superpuesto para funcionar (que la implementación ASIO actual usa por suerte, pero aún es un detalle de implementación). ¡FEO!

2) Opción de socket ASIO personalizado:

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 });

Ventajas: lo suficientemente simple. Rápido. Hermoso (con typedef). Desventajas: depende del detalle de implementación de ASIO, que puede cambiar (pero OTOH todo cambiará eventualmente, y es menos probable que cambie tal detalle que las API públicas sujetas a estandarización). Pero en caso de que esto suceda, deberá escribir una clase de acuerdo con https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/SettableSocketOption.html (que es, por supuesto, una gran PITA gracias a la obvia ingeniería excesiva de esta parte de ASIO) o mejor aún, volver a 1.

3) Use C ++ async / future facilities.

#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;
    }

Ventajas: estándar. Desventajas: siempre comienza un nuevo subproceso (en la práctica), que es relativamente lento (podría ser lo suficientemente bueno para los clientes, pero generará vulnerabilidad DoS para los servidores, ya que los subprocesos y sockets son recursos `` caros ''). No intente usar std :: launch :: deferred en lugar de std :: launch :: async para evitar el lanzamiento de un nuevo hilo, ya que wait_for siempre devolverá future_status :: diferido sin intentar ejecutar el código.

4) El método prescrito por ASIO: utilice únicamente operaciones asíncronas (que en realidad no es la respuesta a la pregunta).

Ventajas: lo suficientemente bueno para los servidores también si no se requiere una gran escalabilidad para transacciones cortas. Desventajas: bastante prolijo (por lo que ni siquiera incluiré ejemplos; consulte los ejemplos de ASIO). Requiere una administración de por vida muy cuidadosa de todos sus objetos utilizados tanto por las operaciones asíncronas como por sus controladores de finalización, lo que en la práctica requiere que todas las clases que contienen y usen dichos datos en las operaciones asincrónicas se deriven de enable_shared_from_this, lo que requiere que todas esas clases se asignen en el montón, lo que significa ( al menos para operaciones cortas) que la escalabilidad comenzará a disminuir después de aproximadamente 16 subprocesos ya que cada asignación de montón / dealloc utilizará una barrera de memoria.

Siguiendo con lo que grepsedawk ha mencionado. Hay algunos ejemplos que muestran cómo cancelar operaciones asincrónicas de larga ejecución después de un período de tiempo, en la sección Tiempos de espera dentro de asio doco. Ejemplos de Boost Asio . El cliente TCP asíncrono me ayudó más.

Happy Asyncing :)

Incluso años después de la pregunta original, todavía no hay una respuesta satisfactoria.

Usar manualmente select no es una buena opción

    El
  1. número de descriptor de archivo debe ser inferior a 1024
  2. FD puede ser falsamente reportado como listo debido a una suma de verificación incorrecta.

Llamar a io_service.run_one () también es una mala idea, porque puede haber otras opciones asíncronas que necesitan un io_service para siempre run () . Y el documento de boost sobre el bloqueo del cliente tcp es difícil de comprender.

Entonces aquí está mi solución. La idea clave es la siguiente:

{
    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);
}

Aquí Semaphore es solo un mutex y una condición_variable.
wait_for es implementado por http: //en.cppreference .com / w / cpp / thread / condition_variable / wait_for

El código completo está en https://github.com/scinart /cpplib/blob/master/include/asio.hpp
Ejemplos en https://github.com/scinart/ cpplib / blob / master / test / test_asio.cpp
Mejor ejemplo en https://github.com/scinart/cpplib/blob /master/test/test_SyncBoostIO.cpp

Puede ajustar las llamadas síncronas en futuros y esperar a que se complete con un tiempo de espera (wait_timeout).

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

Ciertamente, no hay una talla única para todos, pero funciona bien, p. evitando los tiempos de espera de conexión lenta.

En * nix, usarías alarm () para que tu llamada de socket fallara con EINTR

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top