2008-11-15 20 views
23

¿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?¿Cómo establecer un tiempo de espera en los sockets de bloqueo en boost asio?

I.e. Quiero establecer un tiempo de espera en el bloqueo de socket en impulsar asio?

socket.read_some (boost :: :: asio del búfer (pData, maxSize), ERROR_);

Ejemplo: Quiero leer algo de la toma, pero quiero un error si han transcurrido 10 segundos.

Respuesta

8

Bajo Linux/BSD el tiempo de espera en las operaciones de E/S en sockets es apoyado directamente por el sistema operativo. La opción se puede habilitar a través del setsocktopt(). No sé si boost::asio proporciona un método para configurarlo o expone el script de socket para permitirle configurarlo directamente; este último caso no es realmente portable.

Para una mayor abundamiento aquí está la descripción de la página man:

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. 
+1

Esta sería una gran solución, pero no tienen tales opciones de socket. Vea las opciones de socket aquí: http://www.boost.org/doc/libs/1_37_0/doc/html/boost_asio/reference.html –

+0

Boost son buenos pero no perfectos :-) –

+4

Pero por lo que puedo decir, asio's read_some() aún continuará internamente en bucle siempre si no lee nada, por lo que cancela el efecto de SO_RCVTIMEO que puede haber configurado en el socket nativo. Parece que usar select() con socket.native() seguirá siendo la forma más obvia de tener un tiempo de espera excedido. –

-2

En * nix, tendrá que utilizar la alarma() para que su llamada socket fallaría con EINTR

+0

Would no todas sus llamadas de socket, en todas sus hilos fallar con EINTR? Eso suena mal. –

9

Usted 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.

edición: Ha encontrado un fragmento de código para usted que hace esto

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

+1

Este fragmento contiene una llamada a 'io_service :: reset()'. Pero la documentación dice: 'No se debe invocar a esta función mientras haya llamadas pendientes a las funciones run(), run_one(), poll() o poll_one(). – Gabriel

+0

Si este código se va a ejecutar en asio async evento de devolución de llamada, es decir, dentro de algo llamado por 'io_service :: run', sospecho que obtienes un comportamiento indefinido. – Gabriel

6

que tenía la misma pregunta, y después de algunas investigaciones, la solución más limpia más simple que pudiera venir fue para obtener el subyacente socket nativo, 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<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 le sea útil para su situación.

2

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 .Async TCP client helped me the most.

feliz :) Asyncing

13

Cuando se hizo esta pregunta, supongo ASIO no tenía ningún ejemplo de cómo llevar a cabo lo que el PO es necesario, que es el tiempo de espera para una operación de bloqueo tal como una operación de socket de bloqueo. 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.

Timeout a blocking asio tcp operation

Timeout a blocking asio udp operation

La documentación ASIO se ha actualizado, a fin de comprobar hacia fuera para nuevos ejemplos sobre cómo superar algunos de los ASIO 'trampas' usar para tener.

+0

En Windows, 'io_service.run_one()' nunca se bloquea en async_read (al menos en Boost 1.59.0), lo que da como resultado el uso de 100% de la CPU. – rustyx

+3

Los ejemplos son largos porque la documentación es 3x redundante. No considero eso "BIEN comentado". – ChristophK

1

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

usando Selecciona manualmente no es una buena opción número del descriptor

  1. archivo debe ser inferior a 1.024
  2. FD puede informar falsamente como listo debido a la suma de comprobación incorrecta.

llamada io_service.run_one() es también una mala idea, porque puede haber otras opciones asíncronas que necesita una io_service para 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 sólo un mutex y una condition_variable.
wait_for se implementa por http://en.cppreference.com/w/cpp/thread/condition_variable/wait_for

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

Cuestiones relacionadas