¿Cómo establecer un tiempo de espera en los sockets de locking en boost asio?

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

Es decir, ¿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.

En Linux / BSD, el tiempo de espera de las operaciones de E / S en sockets es soportado directamente por el sistema operativo. 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 script del socket para permitirle configurarlo directamente; el último caso no es realmente portable.

En aras de la exhaustividad, 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 (eg, read(2), recvmsg(2), send(2), sendmsg(2)); timeouts have no effect for select(2), poll(2), epoll_wait(2), etc. 

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

Creo que el ejemplo es una gran solución. Las otras soluciones aquí rompen la portabilidad y no aprovechan ioservice. si la portabilidad no es importante y el ioservicio parece tener demasiada sobrecarga –TODO– no debería usar ASIO. No importa qué, tendrá un ioservicio creado (casi todas las funciones de ASIO dependen de él, incluso los conectores de sincronización), así que aproveche.

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

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

La documentación de ASIO se ha actualizado, así que compruébalo para obtener nuevos ejemplos sobre cómo superar algunas de las ‘fallas’ que el uso de ASIO tiene.

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

editar: encontré un fragmento de código para ti que hace esto

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

Tuve la misma pregunta, y después de algunas investigaciones, la solución más simple y limpia que pude encontrar fue obtener el socket nativo subyacente, y hacer una selección hasta que haya 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 de nuevo, esta parece ser la forma más limpia. Por lo que pude ver, asio no proporciona una forma de hacer esto para el uso sincrónico fácilmente. Código:

  // socket here is: boost::shared_ptr 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); ... 

Tal vez esto sea útil para su situación.

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

Happy Asyncing 🙂

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

El uso manual de select no es una buena opción

  1. el número del descriptor de archivo debe ser inferior a 1024
  2. FD puede ser falsamente reportado como listo debido a una sum de comprobación incorrecta.

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

Así que 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 condition_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
Los ejemplos están 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

TL; DR

 socket.set_option(boost::asio::detail::socket_option::integer{ 200 }); 

RESPUESTA COMPLETA Esta pregunta se repite una y otra vez durante muchos años. Las respuestas que vi hasta ahora son bastante malas. Agregaré esta información aquí en una de las primeras apariciones de esta pregunta.

Todo el mundo que intente utilizar ASIO para simplificar su código de red sería perfectamente feliz si el autor simplemente agregara un parámetro de tiempo de espera para todas las funciones de sincronización y asincronización. 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 a este pobre gato hasta ahora, ninguna de ellas especialmente apetecible. Digamos que necesitamos 200 ms de tiempo de espera.

1) Buena (mala) API de socket viejo:

 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 las peculiaridades: – const int for timeout – en Windows el tipo requerido es realmente DWORD, pero afortunadamente el conjunto actual de comstackdores tiene el mismo, por lo que const int funcionará tanto en Win como en Posix world. – (const char *) para el valor. En Windows se requiere const char *, Posix requiere const void *, en C ++ const char * se convertirá en const void * silenciosamente mientras que lo opuesto no es verdadero.

Ventajas: funciona y probablemente siempre funcione ya que el socket API es antiguo y estable. Suficientemente simple. Rápido. Desventajas: técnicamente podría requerir archivos de encabezado apropiados (diferentes en Win e incluso diferentes sabores 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 seguro para tipos. En Windows, se requiere que el socket esté en modo superpuesto para funcionar (que la implementación actual de ASIO utiliza, afortunadamente, pero sigue siendo un detalle de implementación). ¡FEO!

2) Opción de socket ASIO personalizada:

 typedef boost::asio::detail::socket_option::integer 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 de los detalles de la implementación de ASIO, lo que podría cambiar (pero OTOH todo cambiará eventualmente, y es menos probable que esos detalles cambien y las API públicas están sujetas a estandarización). Pero en caso de que esto suceda, tendrá que 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, un PITA principal gracias a la obsolescencia excesiva de esta parte de ASIO) o mejor aún revertir a 1.

3) Utilizar C ++ async / futuras instalaciones.

 #include  #include  //... 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 hilo (en la práctica), que es relativamente lento (podría ser lo suficientemente bueno para los clientes, pero dará lugar a una vulnerabilidad DoS para los servidores, ya que los hilos y los sockets son recursos “caros”). No intente utilizar std :: launch :: deferred en lugar de std :: launch :: async para evitar el nuevo inicio de subprocesos, ya que wait_for siempre devolverá future_status :: deferred sin intentar ejecutar el código.

4) El método prescrito por ASIO: use operaciones asincrónicas solamente (que en realidad no es la respuesta a la pregunta).

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

Puede ajustar las llamadas sincrónicas en futuros y esperar a que finalice 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 sola talla para todos, pero funciona bien para, por ejemplo, eludir tiempos de espera de lentitud de conexión.

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

    Intereting Posts