Pregunta ¿Cómo cerrar forzadamente un socket en TIME_WAIT?


Ejecuto un programa particular en Linux que a veces falla. Si lo abres rápidamente después de eso, escucha 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 inmediatamente que el socket se mueva fuera del estado TIME_WAIT?


109
2017-09-03 12:57


origen


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


Respuestas:


/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 uno solo o un rango de paquetes recibidos del interlocutor.

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

TIME-WAIT - representa a la espera de   suficiente tiempo para pasar para estar seguro       El TCP remoto recibió el acuse de recibo de su conexión.       solicitud de terminación.

Vea el siguiente diagrama de estado TCP: alt text

TCP es un protocolo de comunicación bidireccional, de modo que cuando se establece la conexión, no hay una 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 las renuncias como el cerrador activo, y al otro como el pasivo más cercano. Cuando el cerrador activo envía FIN, el estado pasa a FIN-WAIT-1. Luego recibe un ACK para el FIN enviado y el estado pasa a FIN-WAIT-2. Una vez que recibe FIN también desde el pasivo más cercano, el activo más cercano envía el ACK al FIN y el estado pasa a TIEMPO-ESPERE. En caso de que el cerrador pasivo no haya recibido el ACK con el segundo FIN, retransmitirá el paquete FIN.

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

En realidad, los paquetes faltantes 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 ?, aun así seguiré con mi respuesta original:

/etc/init.d/networking restart

En términos prácticos, lo programaría para que ignore el estado de TIEMPO DE ESPERA usando la opción SO_REUSEADDR como se menciona en 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), adelante y   reutilizarlo de todos modos. Si está ocupado, pero   con otro estado, todavía obtendrá   una dirección ya en uso error. Eso   es útil si su servidor ha sido cerrado   abajo, y luego reiniciado de inmediato   mientras que los enchufes todavía están activos en su   Puerto. Debe tener en cuenta que si   Cualquier dato inesperado entra, puede   confundir su servidor, pero mientras esto   Es posible, no es probable.


139
2017-09-03 13:11



Gran respuesta, pero no la respuesta correcta a su pregunta. Reiniciar la red funcionaría, pero luego reiniciaría, por lo que no puede ser correcto. - Chris Huang-Leaver
@Chris Huang-Leaver, la pregunta es "¿Hay algún programa que puedas ejecutar para forzar inmediatamente que el socket se mueva fuera del estado TIME_WAIT?" Si el reinicio se puede considerar ejecutar un programa, también sería una respuesta correcta. ¿Por qué crees que esto no puede ser correcto? - Eugene Yokota
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 puede llevar 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
oh bien, la próxima vez que algún proceso se cuelgue en SIGTERM, solo aplastaré mi computadora en lugar de arreglarlo. - Longpoke


No sé si tiene el código fuente de ese programa en particular que está ejecutando, pero si es así, simplemente podría 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 el estado TIME_WAIT (a menos que ese socket esté escuchando activamente, vea socket(7)).

Para obtener más información sobre el estado TIME_WAIT, consulte la FAQ Unix socket.


50
2017-09-03 13:17



pero no conseguí el error ya atado. cuando ejecuto el programa de nuevo, 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
Incluso con SO_REUSEADDR, todavía es posible obtener el error "Dirección que ya está en uso". Para más detalles, consulte hea-www.harvard.edu/~fine/Tech/addrinuse.html. - Jingguo Yao
@WMR SO_REUSEADDR no "cierra" un zócalo. Simplemente le permite reutilizar los que ya están abiertos. Así que la pregunta sigue siendo "¿Cómo cerrar por la fuerza un socket en TIME_WAIT? " - Pacerier


Por lo que sé, no hay manera de cerrar forzadamente el zócalo fuera de escribir un mejor manejador de señales en su programa, pero hay un archivo / proc que controla 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).

Incidentalmente, la documentación del kernel le advierte que no cambie ninguno de estos valores sin el 'consejo / solicitud de expertos técnicos'. Lo cual no soy.

El programa debe haberse escrito para intentar un enlace al puerto 49200 y luego aumentar 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 que espere unos segundos y vuelva a intentarlo en el mismo puerto, en lugar de aumentar.


32
2017-09-03 13:24



Creo que los dos segundos ejemplos deberían ser s / rw / tw / I edit, pero carecen de suficiente repetición.
Tomado de la documentación del núcleo: Precaución. Tanto tcp_tw_recycle como tcp_tw_reuse pueden causar problemas. No debe habilitar ni sin comprender 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 a través de nodos que son conscientes de los estados de conexión TCP, como el firewall, NAT o el equilibrador de carga, pueden comenzar a eliminar marcos debido a la configuración. El problema se hará visible cuando haya un número suficientemente grande de conexiones.
Configurándolo para 1 funciona para conexiones futuras, pero ¿qué pasa con las actuales que ya están abiertas? - Pacerier


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

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


16
2017-10-30 17:32



¡Gracias! Esta utilidad funciona muy bien! Me salvó de tener que reiniciar un trabajo largo. - Zanson


Otra opción es usar la opción SO_LINGER con un tiempo de espera de 0. De esta manera, cuando cierra el zócalo se cierra por la fuerza, se envía un RST en lugar de ir al comportamiento de cierre de FIN / ACK. Esto evitará el estado TIME_WAIT y puede ser más apropiado para algunos usos.


3
2018-06-10 22:33



También pierde cualquier información saliente que aún esté 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. Las redes no son confiables, y pelear eso ralentizará las cosas. Una aplicación bloqueada no puede asumir que ningún dato se haya hecho de forma segura. - Tobu
En realidad, lo recomendaría cualquier día, cuando el otro extremo sea una puerta de enlace de bus industrial incrustada con errores que implementa 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 intenta serlo, y empeorarlo no implica mejorar nada, y dejar que TCP haga su trabajo no implica "luchar" contra nada. - user207421


Una solución alternativa sería tener un software confiable de proxy o reenvío de puertos que escuche en el puerto 49200, luego reenviar la conexión a una de las 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 que no se use justo por encima del rango 0-1024. Es menos probable que su sistema utilice un número de puerto más bajo como un puerto efímero.


2
2017-08-21 20:28





TIME_WAIT es el problema más común en la arquitectura del servidor cliente de programación de socket. Esperar unos segundos intentando periódicamente es la mejor solución para ello. Para aplicaciones en tiempo real que necesitan el servidor debe levantarse inmediatamente. Hay una opción SO_REUSEADDR para ellos.


0
2017-10-13 19:07