socket servidor que programa comando cliente chuidiang bloqueante af_unix c unix sockets network-programming

que - socket en c++ cliente servidor linux



select on UDP socket no termina cuando el socket está cerrado, ¿qué estoy haciendo mal? (6)

UDP es un protocolo sin conexión. Como no hay conexión, ninguna se puede romper, por lo que el consumidor no sabe que el productor nunca volverá a enviar.

Puede hacer que el productor envíe un mensaje de "final de transmisión" y que el consumidor finalice al recibirlo.

Estoy trabajando en un sistema Linux (servidor Ubuntu 7.04 con kernel 2.6.20).

Tengo un programa que tiene un hilo (thread1) esperando en una selección para que un socket UDP sea legible. Estoy usando la selección (con mi socket como lectura única y el único exceptofd) en lugar de simplemente llamar a recvfrom porque quiero un tiempo de espera.

De otro hilo, apago y cierro el socket. Si hago esto mientras thread1 está bloqueado en un recvfrom, entonces el recvfrom terminará inmediatamente. Si hago esto mientras thread1 está bloqueado en una selección con un tiempo de espera excedido, la selección NO terminará inmediatamente, pero eventualmente terminará correctamente.

¿Alguien puede decirme por qué la selección no se cierra tan pronto como se cierra el socket? ¿No es eso una excepción? Puedo ver que no es legible (obviamente), pero está cerrado, lo que parece ser excepcional.

Aquí está la apertura del socket (se eliminó todo el manejo de errores para mantener las cosas simples):

m_sockfd = socket(PF_INET, SOCK_DGRAM, 0); struct sockaddr_in si_me; memset((char *) &si_me, 0, sizeof(si_me)); si_me.sin_family = AF_INET; si_me.sin_port = htons(port); si_me.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(m_sockfd, (struct sockaddr *)(&si_me), sizeof(si_me)) < 0) { // deal with error }

Aquí está la instrucción select que se ejecuta thread1:

struct timeval to; to.tv_sec = timeout_ms/1000;// just the seconds portion to.tv_usec = (timeout_ms%1000)*1000;// just the milliseconds // converted to microseconds // watch our one fd for readability or // exceptions. fd_set readfds, exceptfds; FD_ZERO(&readfds); FD_SET(m_sockfd, &readfds); FD_ZERO(&exceptfds); FD_SET(m_sockfd, &exceptfds); int nsel = select(m_sockfd+1, &readfds, NULL, &exceptfds, &to);

ACTUALIZACIÓN: Obviamente (como se indica a continuación), cerrar el zócalo no es una condición excepcional (desde el punto de vista de los seleccionados). Creo que lo que necesito saber es: ¿Por qué? Y, es eso intencional ?.

REALMENTE quiero entender el pensamiento detrás de este comportamiento selecto porque parece contrario a mis expectativas. Por lo tanto, obviamente necesito ajustar mi pensamiento sobre cómo funciona la pila de TCP. Por favor, explícamelo.


Creo que la solución más obvia es que estar cerrado no se considera una condición excepcional. Creo que la raíz del problema es que realmente no estás adoptando la filosofía de select . ¿Por qué diablos estás jugueteando con el socket en otro hilo, eso suena como una receta para el desastre.


Tal vez deberías usar algo más para activar la selección. Tal vez una pipa o algo así.


Yo diría que la diferencia es que recvfrom está intentando activamente leer un mensaje desde un único socket, donde select está esperando que llegue un mensaje, posiblemente en múltiples identificadores, y no necesariamente en identificadores de socket.


¿No podría enviar una señal (por ejemplo, USR2) a la secuencia que provocaría que select () regresara con EINTR? Luego, en el manejador de señal, establezca un indicador indicándole que no reinicie select ()?

Eso eliminaría la necesidad de esperar en múltiples descriptores de archivos, y parece mucho más limpio que usar una tubería para matarlo.


Su código está fundamentalmente roto. Las variaciones en este error son comunes y han causado errores serios con implicaciones de seguridad masivas en el pasado. Esto es lo que te estás perdiendo:

Cuando vaya a cerrar el zócalo, simplemente no hay forma posible de saber si el otro hilo está bloqueado en select o sobre bloquear en select . Por ejemplo, considere lo siguiente:

  1. El hilo va a llamar a select , pero no se programa.
  2. Usted cierra el zócalo.
  3. En un hilo que su código desconoce (tal vez es parte de la administración interna de la memoria de la plataforma o de las funciones internas de registro) una biblioteca abre un socket y obtiene el mismo identificador que el socket que cerró.
  4. El hilo ahora va a select , pero está select en el socket abierto por la biblioteca.
  5. Ataques de desastre.

No debe intentar liberar un recurso mientras otro hilo está, o podría estar, usándolo.