133 votos

¿Cómo cerrar por la fuerza un enchufe en TIME_WAIT?

Ejecuto un programa particular en linux que a veces se bloquea. Si lo abres rápidamente después de eso, escucha en el zócalo 49201 en lugar de 49200 como lo hizo la primera vez. netstat revela que 49200 está en estado de TIME_WAIT.

¿Hay algún programa que pueda ejecutar para forzar inmediatamente a ese enchufe a salir del estado TIME_WAIT?

1 votos

Si está aquí por "demasiados TIME_WAIT en el servidor" , sólo saltar a través de las tres primeras respuestas que evitan la pregunta en lugar de responderla.

0 votos

Acepté lo que era la 4ª respuesta por lo que debería estar en la parte superior ahora

0 votos

166voto

Jedi Master Spooky Puntos 2374
/etc/init.d/networking restart

Permítanme que me explaye. El Protocolo de Control de Transmisión (TCP) está diseñado para ser un protocolo de transmisión de datos bidireccional, ordenado y fiable entre dos puntos finales (programas). En este contexto, el término fiable significa que retransmitirá los paquetes si se pierden en el medio. TCP garantiza la fiabilidad mediante el envío de paquetes de acuse de recibo (ACK) para un solo paquete o un rango de paquetes recibidos del compañero.

Lo mismo ocurre con las señales de control, como la solicitud/respuesta de terminación. RFC 793 define el estado TIME-WAIT de la siguiente manera:

TIME-WAIT - representa la espera de tiempo suficiente para estar seguro de que que el TCP remoto ha recibido el acuse de recibo de su conexión solicitud de finalización de la conexión.

Véase el siguiente diagrama de estado de TCP: alt text

TCP es un protocolo de comunicación bidireccional, por lo que cuando se establece la conexión, no hay diferencia entre el cliente y el servidor. Además, cualquiera de los dos puede llamar a la salida, y ambos pares necesitan estar de acuerdo en el cierre para cerrar completamente una conexión TCP establecida.

Llamemos al primero que se retira como el cerrador activo, y al otro par el cerrador pasivo. Cuando el cerrador activo envía FIN, el estado pasa a FIN-WAIT-1. Luego recibe un ACK por el FIN enviado y el estado pasa a FIN-WAIT-2. Una vez que recibe FIN también del cerrador pasivo, el cerrador activo envía el ACK de FIN y el estado pasa a TIME-WAIT. En caso de que el cerrador pasivo no haya recibido el ACK de la segunda FIN, retransmitirá el paquete FIN.

RFC 793 establece que el TIEMPO DE SALIDA sea el doble del Tiempo Máximo de Vida del Segmento, o 2MSL. Como el MSL, el tiempo máximo que un paquete puede vagar por Internet, está fijado en 2 minutos, 2MSL es 4 minutos. Como no hay ACK a un ACK, el cerrador activo no puede hacer otra cosa que esperar 4 minutos si se adhiere al protocolo TCP/IP correctamente, por si el emisor pasivo no ha recibido el ACK a su FIN (teóricamente).

En realidad, los paquetes perdidos son probablemente raros, y muy raros si todo ocurre dentro de la LAN o dentro de una sola máquina.

Para responder a la pregunta textualmente, ¿Cómo a la fuerza cerrar un socket en TIME_WAIT?, seguiré manteniendo mi respuesta original:

/etc/init.d/networking restart

En la práctica, yo lo programaría para que ignore el estado TIME-WAIT usando la opción SO_REUSEADDR como mencionó WMR. ¿Qué hace exactamente SO_REUSEADDR?

Esta opción de socket le dice al kernel que incluso si este puerto está ocupado (en
el estado TIME_WAIT), siga adelante y reutilizarlo de todos modos. Si está ocupado, pero con otro estado, todavía obtendrá un error de dirección ya en uso. Es es útil si su servidor ha sido apagado y luego se reinicia de inmediato mientras los sockets están todavía activos en su puerto. Debe tener en cuenta que si datos inesperados, puede confundir a su servidor. confundir a su servidor, pero aunque esto es posible, no es probable.

12 votos

Gran respuesta, pero no es la respuesta correcta a su pregunta. Reiniciar la red funcionaría, pero entonces también lo haría reiniciar, así que esto no puede ser correcto.

4 votos

@Chris Huang-Leaver, la pregunta es "¿Existe algún programa que pueda ejecutar para forzar inmediatamente a ese socket a salir del estado TIME_WAIT?" si reiniciar pudiera considerarse como ejecutar un programa, entonces también sería una respuesta correcta. ¿Por qué crees que esto no puede ser correcto?

9 votos

WMR tiene la respuesta más útil (que es lo que yo hago cuando me encuentro con este tipo de problemas). Reiniciar la red es demasiado drástico para ser la solución, y podría tomar más tiempo que simplemente esperar a que el tiempo de espera.La respuesta correcta a su pregunta es "No", pero SO no le permitirá escribir dos letras respuestas :-)

54voto

WMR Puntos 701

No sé si tienes el código fuente de ese programa en particular que estás ejecutando, pero si es así podrías simplemente establecer SO_REUSEADDR a través de setsockopt(2) que le permite enlazar en la misma dirección local incluso si el socket está en estado TIME_WAIT (a menos que ese socket esté escuchando activamente, véase socket(7) ).

Para más información sobre el estado TIME_WAIT, véase el Preguntas frecuentes sobre los sockets de Unix .

0 votos

Cuando ejecuto el programa de nuevo, escucha en el puerto (123456) también puedo ver que el sistema muestra TIME_WAIT para ese puerto, pero todavía puedo conectarme.

3 votos

Incluso con SO_REUSEADDR, es posible obtener el error "Dirección ya utilizada". Para más detalles, consulte hea-www.harvard.edu/~fine/Tech/addrinuse.html .

0 votos

@WMR SO_REUSEADDR no "cierra" un socket. Simplemente permite reutilizar los que ya están abiertos. Así que la pregunta sigue siendo "¿Cómo cerrar forzosamente un socket en TIME_WAIT ?"

36voto

Ben Hamill Puntos 655

Hasta donde yo sé, no hay manera de cerrar el socket a la fuerza fuera de escribir un mejor manejador de señales en su programa, pero hay un archivo /proc que controla cuánto tiempo toma el tiempo de espera. El archivo es

/proc/sys/net/ipv4/tcp_tw_recycle

y puedes establecer el tiempo de espera a 1 segundo haciendo esto:

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

Sin embargo, esta página contiene una advertencia sobre posibles problemas de fiabilidad al establecer esta variable.

También hay un archivo relacionado

/proc/sys/net/ipv4/tcp_tw_reuse

que controla si los sockets TIME_WAIT pueden ser reutilizados (presumiblemente sin ningún tiempo de espera).

Por cierto, la documentación del kernel advierte que no hay que cambiar ninguno de estos valores sin "asesoramiento/solicitudes de expertos técnicos". Cosa que yo no soy.

El programa debe haber sido escrito para intentar un enlace al puerto 49200 y luego incrementar en 1 si el puerto ya está en uso. Por lo tanto, si tienes el control del código fuente, podrías cambiar este comportamiento para esperar unos segundos e intentar de nuevo en el mismo puerto, en lugar de incrementar.

0 votos

creo que los dos segundos ejemplos deberían ser s/rw/tw/ lo editaría, pero me falta la rep.

1 votos

Tomado de la documentación del kernel: Precaución. Tanto tcp_tw_recycle como tcp_tw_reuse pueden causar problemas. No debe habilitar ninguno de los dos sin entender la topología de la red entre los nodos que están usando o son usados por el nodo donde el parámetro está habilitado. Las conexiones que pasan por nodos que conocen los estados de las conexiones TCP, como el cortafuegos, el NAT o el equilibrador de carga, pueden empezar a dejar caer tramas debido a la configuración. El problema se hará visible cuando haya un número suficiente de conexiones.

0 votos

Ponerlo en 1 funciona para futuras conexiones, pero ¿qué pasa con las actuales que ya están abiertas?

21voto

akostadinov Puntos 380

En realidad hay una manera de matar una conexión - killcx . Afirman que funciona en cualquier estado de la conexión (cosa que no he verificado). Sin embargo, es necesario conocer la interfaz en la que se produce la comunicación, parece que asume eth0 por defecto.

ACTUALIZACIÓN: otra solución es cortador que viene en los repositorios de algunas distros de linux.

0 votos

Killcx simplemente no funciona para mí. Cutter requiere un firewall para interactuar.

0 votos

¿Hay algún comando disponible en Redhat 7 o superior para conseguirlo?

0 votos

@nitinr708 nada fuera de la caja afaik :/ tienes que instalar desde los proyectos de aguas arriba.

3voto

Otra opción es utilizar la opción SO_LINGER con un tiempo de espera de 0. De esta manera, cuando se cierra el socket se cierra forzosamente, enviando un RST en lugar de entrar en el comportamiento de cierre FIN/ACK. Esto evitará el estado TIME_WAIT, y puede ser más apropiado para algunos usos.

3 votos

También se pierden los datos de salida que todavía están en tránsito, y puede causar un error en el otro extremo. No se recomienda.

0 votos

@EJP Fracasar pronto es casi siempre la decisión correcta. La red no es fiable, y luchar contra ella ralentizará las cosas. Una aplicación que se bloquea no puede suponer que los datos salgan a salvo.

2 votos

En realidad, recomendaría esto cualquier día cuando el otro punto final es una pasarela de bus industrial embebida y con errores que implementa su propio transporte fiable de capa de aplicación sobre TCP, donde dicho transporte impide que la conexión se cierre nunca a menos que reciba RST y, por lo tanto, llena el límite de conexión en esa pasarela. Ya está. Te he dado un ejemplo muy concreto y muy real que, lamentablemente, requiere recurrir a hacks como este.

EnMiMaquinaFunciona.com

EnMiMaquinaFunciona es una comunidad de administradores de sistemas en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros sysadmin, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X