2011-06-10 17 views
9

Tengo un objeto de C++ que crea un hilo para leer de un socket UDP bloqueo:manera adecuada para cerrar un socket UDP bloqueo

mRunning.store(true); 
while (mRunning.load(boost::memory_order_consume)) { 
    ... 
    int size = recvfrom(mSocket, buf, kTextBufSize , 0, 
         (struct sockaddr *) &packet->mReplyAddr.mSockAddr, (socklen_t*)&packet->mReplyAddr.mSockAddrLen); 

    if (size > 0) { 
     //do stuff 
    } 
} 
return 0; 

(mRunning es un impulso :: atómica) destructor del objeto se llama desde otro hilo y hace esto:

mRunning.store(false); 
#ifdef WIN32 
    if (mSocket != -1) closesocket(mSocket); 
#else 
    if (mSocket != -1) close(mSocket); 
#endif 
pthread_join(mThread, NULL); 

esto parece funcionar, pero uno de mis colegas sugirió que podría ser un problema si recv se interrumpe en medio de la lectura de algo. ¿Este hilo es seguro? ¿Cuál es la forma correcta de cerrar un socket UDP de bloqueo? (Debe ser multiplataforma OSX/Linux/Windows)

Respuesta

1

Bueno recvfrom en sí mismo es thread-safe. IIRC todas las funciones de socket son. La pregunta es:

  • lo que va a ocurrir si se tira el descriptor de debajo recvfrom mientras se está copiando datos a su memoria intermedia?

Es una buena preguntapero dudan de la norma dice nada acerca de esto (también dudo que el manual específico de una aplicación dice nada al respecto). Por lo que cualquier aplicación es libre de:

  • completar la operación (tal vez porque no necesita el descriptor más, o porque está haciendo un poco de recuento de referencias fresca o algo más)
  • Hacer el recvfrom fallar y devolver una -1 (ENOTSOCK, EINVAL?)
  • Se bloquea espectacularmente porque los buffers y las estructuras internas de datos se liberan por el close.

Obviamente, esto es sólo especulación (me he equivocado antes, muchas veces), pero a menos que encuentre algo en la norma para apoyar la idea de que se puede cerrar el socket mientras recibe a través de él, eres no es seguro.

Entonces, ¿qué puedes hacer? Lo más seguro: utilice un mecanismo de sincronización para asegurarse de que solo close el zócalo después del recvfrom esté hecho (semáforos, mutexes, lo que sea).

Personalmente me gustaría hacer un UP en un semáforo después de la recvfrom y una DOWN antes de la close.

0

Su colega tiene razón, los enchufes de refuerzo no son seguros para subprocesos.

Sus opciones;

  1. uso ASIO (hacer esto uno)
  2. tiempo de espera de bloqueo de llamadas. Esto no es realmente portátil, aunque podría funcionar.
+2

No son enchufes de refuerzo, son enchufes de posix. Boost es solo para asegurar que el cambio de mRunning bool sea atómico. Lamento haber sido destrozado cuando lo pegué y no me di cuenta. –

5

Podría haber muchos problemas diferentes. Moviendo mi aplicación de una versión de FreeBSD a otra encontré que close() funcionaba normalmente en kernel anterior y colgaba close() hasta que algo retornaba de recv() en newer.Y OSX está basado en FreeBSD :)

La forma portátil de cerrar tomas de diferentes roscas es crear tuberías y bloquearlas no en recv(), sino en select(). Cuando necesite cerrar el socket, escriba algo para pipe, select() se desbloqueará y podrá hacer close() de forma segura.

+1

No creo que esto funcione realmente en plataformas cruzadas, ya que select() en windows parece solo soportar sockets, no pipes. –

+1

En Windows, puede tener un código separado que usará WSAEventSelect() y WaitForMultipleObjects(). – blaze

Cuestiones relacionadas