¿Cómo cerrar a la fuerza un socket en TIME_WAIT?


113

Ejecuto un programa particular en Linux que a veces falla. Si lo abre rápidamente después de eso, escuchará en el socket 49201 en lugar de 49200 como lo hizo la primera vez. netstat revela que 49200 está en un estado TIME_WAIT.

¿Hay algún programa que pueda ejecutar para forzar de inmediato a ese socket a salir del estado TIME_WAIT?


1
Si está aquí debido a "demasiados TIME_WAITen el servidor" , omita las tres primeras respuestas que evitan la pregunta en lugar de responderla.
Pacerier

Respuestas:


148
/etc/init.d/networking restart

Déjame elaborar. El Protocolo de control de transmisión (TCP) está diseñado para ser un protocolo de transmisión de datos bidireccional, ordenado y confiable entre dos puntos finales (programas). En este contexto, el término confiable significa que retransmitirá los paquetes si se pierde en el medio. TCP garantiza la confiabilidad al devolver los paquetes de acuse de recibo (ACK) para un solo paquete o un rango de paquetes recibidos del par.

Esto es igual para las señales de control, como la solicitud / respuesta de terminación. RFC 793 define el estado de ESPERA DE TIEMPO como el siguiente:

TIME-WAIT: representa esperar el tiempo suficiente para asegurarse de que el TCP remoto recibió el acuse de recibo de su solicitud de finalización de la conexión.

Consulte el siguiente diagrama de estado TCP: texto alternativo

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 dejar de fumar y ambos pares deben acordar el cierre para cerrar completamente una conexión TCP establecida.

Llamemos al primero para llamar a los abandonos como el cerrador activo, y el otro mira al 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 al FIN y el estado pasa a ESPERA DE TIEMPO. En caso de que el cerrador pasivo no haya recibido el ACK al segundo FIN, retransmitirá el paquete FIN.

RFC 793 establece que el TIEMPO DE ESPERA sea el doble de la vida útil máxima del segmento, o 2MSL. Dado que MSL, el tiempo máximo que un paquete puede deambular por Internet, se establece en 2 minutos, 2MSL es de 4 minutos. Como no hay ACK en un ACK, el cerrador activo no puede hacer nada más que esperar 4 minutos si se adhiere correctamente al protocolo TCP / IP, en caso de que el remitente pasivo no haya recibido el ACK en su FIN (en teoría) .

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

Para responder la pregunta al pie de la letra, ¿Cómo cerrar a la fuerza un socket en TIME_WAIT ?, aún me atendré a mi respuesta original:

/etc/init.d/networking restart

Hablando prácticamente, lo programaría para que ignore el estado de ESPERA DE TIEMPO 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), continúe y reutilícelo de todos modos. Si está ocupado, pero con otro estado, aún obtendrá una dirección de error en uso. Es útil si su servidor se apagó y luego se reinició de inmediato mientras los sockets aún están activos en su puerto. Debe tener en cuenta que si ingresa información inesperada, puede confundir a su servidor, pero si bien es posible, no es probable.


8
Gran respuesta, pero no la respuesta correcta a su pregunta. Reiniciar la red funcionaría, pero luego reiniciaría, por lo que esto no puede ser correcto.
Chris Huang-Leaver

3
@Chris Huang-Leaver, la pregunta es "¿Hay algún programa que pueda ejecutar para forzar inmediatamente que el socket salga del estado TIME_WAIT?" Si el reinicio podría considerarse ejecutar un programa, entonces también sería una respuesta correcta. ¿Por qué crees que esto no puede ser correcto?
Eugene Yokota

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

66
oh está bien, la próxima vez que se cuelgue algún proceso en SIGTERM, simplemente romperé mi computadora en lugar de arreglarlo.
Longpoke

La generalización de esto es "reiniciar los servicios de red". La ubicación específica /etc/init.d/networkinges específica de la plataforma (¿Debian?), Por lo que la línea de comandos precisa será diferente (a veces bastante radical) para otros sistemas. Estoy de acuerdo con otros comentaristas en que esto parece una exageración severa y obviamente perjudicial para cualquier servicio de red no relacionado.
tripleee

51

No sé si tiene el código fuente de ese programa en particular que está ejecutando, pero si es así, puede configurar SO_REUSEADDR a través de lo setsockopt(2)cual le permite enlazar en la misma dirección local incluso si el socket está en estado TIME_WAIT (a menos que socket está escuchando activamente, ver socket(7)).

Para obtener más información sobre el estado TIME_WAIT, consulte las preguntas frecuentes sobre el socket Unix .


pero no recibí el error ya vinculado. cuando ejecuto el programa nuevamente, escucha en la publicación (123456) también puedo ver que el sistema muestra TIME_WAIT para ese puerto pero aún puedo conectarme. ¿por qué?
Jayapal Chandran

2
Incluso con SO_REUSEADDR, todavía es posible obtener el error "Dirección ya en uso". Para más detalles, consulte hea-www.harvard.edu/~fine/Tech/addrinuse.html .
Jingguo Yao

@WMR SO_REUSEADDRno "cierra" un socket. Simplemente le permite reutilizar los que ya están abiertos. Entonces la pregunta sigue siendo "¿Cómo cerrar a la fuerza un zócalo TIME_WAIT?
Pacerier

Esta es la respuesta correcta, pero la pregunta no era totalmente correcta. Al menos resolvió mi problema muy bien (no como reiniciar toda la red rompiendo todas las demás conexiones también).
V-Mark

SO_REUSEADDRdejare bind()proceder; pero si luego quieres escuchar ese socket, listen()te devolverá EADDRINUSElo mismo. En otras palabras, esta respuesta puede ayudar al software del cliente a usar puertos efímeros, pero no resuelve el problema del software del servidor.
Será el

33

Hasta donde sé, no hay forma de cerrar el socket por la fuerza fuera de escribir un mejor controlador de señal en su programa, pero hay un archivo / proc que controla cuánto tiempo tarda el tiempo de espera. El archivo es

/proc/sys/net/ipv4/tcp_tw_recycle

y puede establecer el tiempo de espera en 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 confiabilidad al configurar esta variable.

También hay un archivo relacionado

/proc/sys/net/ipv4/tcp_tw_reuse

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

Por cierto, la documentación del núcleo le advierte que no cambie ninguno de estos valores sin 'consejos / solicitudes de expertos técnicos'. Lo cual no soy.

El programa debe haberse escrito para intentar un enlace al puerto 49200 y luego incrementarlo en 1 si el puerto ya está en uso. Por lo tanto, si tiene el control del código fuente, puede cambiar este comportamiento para esperar unos segundos e intentar nuevamente en el mismo puerto, en lugar de aumentar.


Creo que los segundos dos ejemplos deberían ser s / rw / tw / que editaría, pero carece de suficiente representación

1
Tomado de la documentación del kernel: Precaución. Tanto tcp_tw_recycle como tcp_tw_reuse pueden causar problemas. No debe habilitarlo sin comprender la topología de la red entre los nodos que están utilizando o utilizan el nodo donde está habilitado el parámetro. Las conexiones que pasan por nodos que conocen los estados de conexión TCP, como firewall, NAT o balanceador de carga pueden comenzar a soltar marcos debido a la configuración. El problema se hará visible cuando haya un número suficientemente grande de conexiones.

Establecerlo para que 1funcione para conexiones futuras, pero ¿qué pasa con las actuales que ya están abiertas?
Pacerier

18

En realidad, hay una manera de eliminar una conexión: killcx . Afirman que funciona en cualquier estado de la conexión (que no he verificado). Sin embargo, debe conocer la interfaz donde se produce la comunicación, parece asumir eth0 por defecto.

ACTUALIZACIÓN: otra solución es cutter que viene en algunos repositorios de distribuciones de Linux.


3

Otra opción es usar la opción SO_LINGER con un tiempo de espera de 0. De esta manera, cuando cierra el socket se cierra por la fuerza, se envía 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.


2
También pierde los datos salientes que aún están en tránsito y puede causar un error en el otro extremo. No recomendado.
user207421

@EJP Fallar temprano es casi siempre la decisión correcta. La conexión en red no es confiable, y luchar contra eso ralentizará las cosas. Una aplicación bloqueada no puede asumir que ningún dato salió de manera segura.
Tobu

1
En realidad, lo recomendaría cualquier día cuando el otro punto final sea una puerta de enlace de bus industrial incrustada que implemente su propio transporte confiable de capa de aplicación sobre TCP, donde dicho transporte evita que la conexión se cierre a menos que reciba RST y, por lo tanto, se llene El límite de conexión en esa puerta de enlace. Ahí. Te di un ejemplo muy específico y muy real que, lamentablemente, requiere recurrir a hacks como este.
andyn

@Tobu Networking no es confiable, pero TCP trata de serlo, y empeorarlo no constituye mejorar nada, y dejar que TCP haga su trabajo no constituye nada de 'lucha'.
user207421

2

Una solución alternativa sería tener un proxy confiable o un software de reenvío de puertos que escuche en el puerto 49200, luego reenvíe la conexión a una de varias instancias de su programa menos confiable usando diferentes puertos ... HAPROXY viene a la mente.

Por cierto, el puerto en el que se conecta es bastante alto. Puede intentar usar uno sin usar justo por encima del rango 0-1024. Es menos probable que su sistema use un número de puerto más bajo como un puerto efímero.


0

TIME_WAIT es el problema más común en la arquitectura del servidor del cliente de programación de sockets. Espere unos segundos, intentarlo periódicamente es la mejor solución para ello. Para las aplicaciones en tiempo real, necesitan que el servidor se levante de inmediato. Hay una opción SO_REUSEADDR para ellos.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.