2012-05-16 9 views
6

Estoy escribiendo un servidor con la biblioteca asio de boost. El servidor maneja muchas conexiones simultáneas utilizando una colección de objetos Connection (una clase contenedora alrededor de boost :: asio :: tcp :: socket). Dentro de la clase Connection, el socket se lee constantemente utilizando socket.async_read_some (...) y cada vez que se invoca el manejador de lectura con datos nuevos, se llama inmediatamente a socket.async_read_some() para que se lean más datos.boost :: asio :: tcp :: socket Cierre y cancele sin llamar a los controladores

Ahora, el servidor puede decidir desconectar un cliente por algún motivo, por lo que lo más lógico es llamar a connection.close() que a su vez llama a socket.close(), lo que provocará que todas las operaciones asincrónicas pendientes ser cancelado Esto hace que el controlador de lectura (vinculado a un método dentro de la clase Connection) se invoque con boost :: asio :: error :: operation_aborted. Y mi problema es: no quiero que esto suceda.

Después de socket.close(), me gustaría destruir el socket y la conexión y luego quitar su puntero de la lista de clientes activos del servidor. Sin embargo, el controlador de lectura no se invocará hasta la próxima iteración de io_service.run(), lo que significa que no puedo destruir inmediatamente el socket o el controlador de lectura que pasé a socket.async_read_some() hasta que se haya invocado el controlador con el error. Así que tengo que retrasar la destrucción de esos objetos de alguna manera; eso es molesto.

¿Existe una forma segura de cualquiera

  • Cancelar las operaciones pendientes asincrónicos sin ningún controlador que se debe invocar, por lo que puede destruir de manera segura la toma inmediatamente después de Socket.close(), o
  • a saber con seguridad cuándo no se pueden llamar potencialmente más manipuladores

¿O me estoy acercando a esto completamente de la manera incorrecta?

Respuesta

4

Cuando se completa una operación async, ya sea con éxito o con error, se invoca su controlador de finalización. Esta es una garantía importante, y no creo que sea una buena idea tratar de "piratear" este comportamiento. El problema que encontraste en tu caso de uso generalmente se resuelve utilizando shared_ptr (shared_from_this idiom): vincula shared_ptr<Connection> a los manejadores, no emite otro async_read cuando recibas operation_aborted (o algún otro error), de modo que cuando todos los manejadores se hacen, el objeto Connection se destruye con su socket.

+0

Esto suena muy bien. Seguro que no uso shared_ptr lo suficiente y ni siquiera sabía sobre enable_shared_from_this. Solía ​​hacer esto: "socket.async_connect (endpoint, boost :: bind (& Connection :: done_connect, this, _1))", así que ahora solo agregaría un adicional "shared_from_this()" a la llamada de enlace y actualizar el ¿firma de método para un shared_ptr adicional aunque el shared_ptr real ni siquiera se usaría en el controlador? Es decir. el shared_ptr está ahí para garantizar que el objeto aún exista. Suena bien? – jlh

+0

Al menos lo implementé de esta manera y funciona muy bien ahora. ¡Muchas gracias! – jlh

+0

@jlh, no es del todo correcto decir que shared_ptr "no se usaría en el controlador". Si vincula una función de miembro a shared_ptr, se almacena dentro de la carpeta (el functor creado con bind()), y se le quita la referencia en la invocación de functor. Como resultado, el pointee vive mientras el functor viva. –

Cuestiones relacionadas