¿Cómo elimino una conexión de socket CLOSE_WAIT?


91

He escrito un pequeño programa que interactúa con un servidor en un puerto específico. El programa funciona bien, pero:

Una vez que el programa terminó inesperadamente, y desde entonces esa conexión de socket se muestra en CLOSE_WAITestado. Si trato de ejecutar un programa, se cuelga y tengo que forzarlo a cerrar, lo que acumula aún más CLOSE_WAIT conexiones de socket.

¿Hay alguna forma de limpiar estas conexiones?


4
No puedes (y no debes). CLOSE_WAIT es un estado definido por TCP para las conexiones que se cierran a la espera de que la contraparte lo reconozca.
vonbrand

1
Vea también unix.stackexchange.com/questions/10106/… ... que no votaré como un duplicado, porque terminaría cerrando la pregunta como fuera de tema.
derobert

3
@vonbrand No, no lo es, es exactamente lo contrario. Es el estado de una conexión que ya ha sido cerrada por el par y está esperando que la aplicación local cierre su final.
Marqués de Lorne

Si está utilizando Commons HttpClient, nuxeo.com/blog/… tiene mucha información relevante. De RFC 2616, Sección 14: Las aplicaciones HTTP / 1.1 que no admiten conexiones persistentes DEBEN incluir la opción de conexión "cerrar" en cada mensaje.
Mayank Ahuja

Respuestas:


79

CLOSE_WAITsignifica que su programa aún se está ejecutando y no ha cerrado el socket (y el kernel está esperando que lo haga). Agregue -ppara netstatobtener el pid y luego elimínelo con más fuerza (con SIGKILLsi es necesario). Eso debería deshacerse de sus CLOSE_WAITenchufes. También puede usar pspara encontrar el pid.

SO_REUSEADDRes para servidores y TIME_WAITsockets, por lo que no se aplica aquí.


2
bueno ... detener el proceso puede no ser lo mejor si ese programa abre mucha conexión, solo algunos de los que permanecen en "CLOSE_WAIT": en ese caso, matar el proceso puede ser completamente imposible o inapropiado (el programa aún funciona y proporciona servicios, con esas otras conexiones). Simplemente cerrar la conexión pendiente sería mucho más apropiado. pero de hecho suele ser el programa en sí el que no cierra localmente el connectino (CLOSE_WAIT significa que recibió 'FIN' desde el otro extremo y el programa solo tiene que cerrar la conexión localmente). Un informe de error puede ser apropiado
Olivier Dulac

40

Como lo describe Crist Clark .

CLOSE_WAIT significa que el extremo local de la conexión ha recibido un FIN del otro extremo, pero el sistema operativo está esperando que el programa en el extremo local realmente cierre su conexión.

El problema es que su programa que se ejecuta en la máquina local no cierra el socket. No es un problema de ajuste de TCP. Una conexión puede (y muy correctamente) permanecer en CLOSE_WAIT para siempre mientras el programa mantiene abierta la conexión.

Una vez que el programa local cierra el socket, el sistema operativo puede enviar el FIN al extremo remoto que lo cambia a LAST_ACK mientras espera el ACK del FIN. Una vez que se recibe, la conexión finaliza y se elimina de la tabla de conexiones (si su extremo está en CLOSE_WAIT, no termina en el estado TIME_WAIT).


4
como cerrar el enchufe ??
Divyang Shah

1
Cierras la manija que tienes al enchufe que abriste. Utilice close()o closesocket(), según la plataforma que esté utilizando.
Remy Lebeau

8

También tengo el mismo problema con un servidor Tomcat más reciente (7.0.40). No responde una vez durante un par de días.

Para ver las conexiones abiertas, puede usar:

sudo netstat -tonp | grep jsvc | grep --regexp="127.0.0.1:443" --regexp="127.0.0.1:80" | grep CLOSE_WAIT

Como se mencionó en esta publicación , puede usar /proc/sys/net/ipv4/tcp_keepalive_timepara ver los valores. El valor parece estar en segundos y el valor predeterminado es 7200 (es decir, 2 horas).

Para cambiarlos, debe editarlos /etc/sysctl.conf.

Open/create `/etc/sysctl.conf`
Add `net.ipv4.tcp_keepalive_time = 120` and save the file
Invoke `sysctl -p /etc/sysctl.conf`
Verify using `cat /proc/sys/net/ipv4/tcp_keepalive_time`

4
la respuesta es confusa. Dijiste que los estados de no respuesta han desaparecido durante varios días ... pero luego también intentas establecer el tiempo de mantener vivo en solo 120 segundos. incluso con el valor predeterminado (7200 segundos), no debería durar varios días, ¿verdad?
fanchyna

8

Aunque demasiadas conexiones CLOSE_WAIT significan que hay algo mal en su código en la primera y esto no se acepta como una buena práctica.

Es posible que desee consultar: https://github.com/rghose/kill-close-wait-connections

Lo que hace este script es enviar el ACK que estaba esperando la conexión.

Esto es lo que funcionó para mí.


envía act al socket de espera de cierre. con no funciona .. si funciona, ¿por qué?
Chinaxing

Supongo que el sistema operativo ya envió el FIN al host remoto. El host remoto probablemente no pueda responder con el ACK que espera el socket.
espejismo

sí, eso es correcto (del código del kernel). pero también dudo de la SEQ del paquete que envías, que es "10", ¿el kernel no lo comprueba?
Chinaxing

Probablemente no. Creo que lo intenté con muchos números aleatorios y parecieron funcionar.
espejismo

3

Cabe mencionar que la Socketinstancia tanto en el cliente como en el servidor debe invocar explícitamente close(). Si solo uno de los extremos invoca close()entonces, el conector permanecerá en estado CLOSE_WAIT.


3

Puede cerrar sockets a la fuerza con el sscomando; el sscomando es una herramienta que se usa para volcar estadísticas de sockets y muestra información de manera similar (aunque más simple y rápida) a netstat.

Para matar cualquier socket en el estado CLOSE_WAIT, ejecute esto (como root)

$ ss --tcp state CLOSE-WAIT --kill

1

También vale la pena señalar que si su programa genera un nuevo proceso, ese proceso puede heredar todos sus identificadores abiertos. Incluso después de que se cierre su propio programa, esos identificadores heredados pueden seguir vivos a través del proceso hijo huérfano. Y no necesariamente aparecen de la misma manera en netstat. Pero de todos modos, el socket permanecerá en CLOSE_WAIT mientras este proceso hijo esté vivo.

Tuve un caso en el que estaba ejecutando ADB. El propio ADB genera un proceso de servidor si aún no se está ejecutando. Esto heredó todos mis identificadores inicialmente, pero no apareció como propietario de ninguno de ellos cuando estaba investigando (lo mismo era cierto para macOS y Windows, no estoy seguro de Linux).

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.