Comment supprimer une connexion socket CLOSE_WAIT

91

J'ai écrit un petit programme qui interagit avec un serveur sur un port spécifique. Le programme fonctionne bien, mais:

Une fois que le programme s'est terminé de manière inattendue, et depuis que cette connexion socket est affichée dans l' CLOSE_WAITétat. Si j'essaie d'exécuter un programme, il se bloque et je dois le forcer à se fermer, ce qui accumule encore plus de CLOSE_WAIT connexions de socket.

Existe-t-il un moyen de rincer ces connexions?

Dilletante
la source
4
Vous ne pouvez pas (et ne devriez pas). CLOSE_WAIT est un état défini par TCP pour les connexions fermées en attendant que l'homologue l'acquitte.
vonbrand
1
Voir aussi unix.stackexchange.com/questions/10106/… ... que je ne voterai pas comme doublon, car cela finirait par fermer la question comme hors sujet.
derobert
3
@vonbrand Non, ce n'est pas le cas, c'est exactement le contraire. C'est l'état d'une connexion qui a déjà été fermée par l'homologue et qui attend que l'application locale ferme sa fin.
Marquis of Lorne
Si vous utilisez Commons HttpClient, nuxeo.com/blog/… a beaucoup d'informations pertinentes. D'après la RFC 2616, section 14: les applications HTTP / 1.1 qui ne prennent pas en charge les connexions persistantes DOIVENT inclure l'option de connexion "close" dans chaque message.
Mayank Ahuja

Réponses:

79

CLOSE_WAITsignifie que votre programme est toujours en cours d'exécution, et n'a pas fermé le socket (et le noyau attend qu'il le fasse). Ajoutez -pà netstatpour obtenir le pid, puis tuez-le plus énergiquement (avec SIGKILLsi nécessaire). Cela devrait vous débarrasser de vos CLOSE_WAITprises. Vous pouvez également utiliser pspour trouver le pid.

SO_REUSEADDRest pour les serveurs et les TIME_WAITsockets, donc ne s'applique pas ici.

derobert
la source
2
eh bien ... tuer le processus peut ne pas être le meilleur si ce programme ouvre beaucoup de connexions, seuls quelques-uns de ceux qui restent dans "CLOSE_WAIT": dans ce cas, tuer le processus peut être complètement impossible ou inapproprié (le programme fonctionne toujours et fournit des services, avec ces autres connexions). La simple fermeture de la connexion en attente serait beaucoup plus appropriée. mais en effet c'est généralement le programme lui-même qui ne ferme pas localement le connectino (CLOSE_WAIT signifie qu'il a reçu 'FIN' de l'autre extrémité et le programme doit juste fermer la connexion localement). Un rapport de bogue peut être approprié
Olivier Dulac
40

Comme décrit par Crist Clark .

CLOSE_WAIT signifie que l'extrémité locale de la connexion a reçu un FIN de l'autre extrémité, mais que le système d'exploitation attend que le programme à l'extrémité locale ferme réellement sa connexion.

Le problème est que votre programme exécuté sur la machine locale ne ferme pas le socket. Ce n'est pas un problème de réglage TCP. Une connexion peut (et à juste titre) rester indéfiniment dans CLOSE_WAIT pendant que le programme maintient la connexion ouverte.

Une fois que le programme local ferme le socket, le système d'exploitation peut envoyer le FIN à l'extrémité distante qui vous transforme en LAST_ACK pendant que vous attendez l'ACK du FIN. Une fois que cela est reçu, la connexion est terminée et abandonne la table de connexion (si votre extrémité est dans CLOSE_WAIT, vous ne vous retrouvez pas dans l'état TIME_WAIT).

user2618402
la source
4
comment fermer la prise ??
Divyang Shah
1
Vous fermez la poignée que vous avez sur la prise que vous avez ouverte. Utilisez close()ou closesocket(), selon la plate-forme que vous utilisez.
Remy Lebeau
8

J'ai également le même problème avec un tout dernier serveur Tomcat (7.0.40). Il ne répond plus une fois pendant quelques jours.

Pour voir les connexions ouvertes, vous pouvez utiliser:

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

Comme mentionné dans cet article , vous pouvez utiliser /proc/sys/net/ipv4/tcp_keepalive_timepour afficher les valeurs. La valeur semble être en secondes et par défaut à 7200 (soit 2 heures).

Pour les modifier, vous devez les modifier /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`
Amil Waduwawara
la source
4
la réponse est déroutante. vous avez dit que les états non réactifs ont disparu pendant plusieurs jours ... mais vous essayez également de régler le temps de maintien en vie à seulement 120 secondes. même avec la valeur par défaut (7200 sec), cela ne devrait pas durer plusieurs jours, non?
fanchyna du
8

Même si un trop grand nombre de connexions CLOSE_WAIT signifie qu'il y a un problème avec votre code dans le premier et que cela n'est pas une bonne pratique.

Vous voudrez peut-être vérifier: https://github.com/rghose/kill-close-wait-connections

Ce que fait ce script est d'envoyer l'ACK que la connexion attendait.

C'est ce qui a fonctionné pour moi.

mirage
la source
vous envoyez act au socket close-wait. avec ne fonctionne pas .. si fonctionne, pourquoi?
Chinaxing
Je suppose que le système d'exploitation a déjà envoyé le FIN à l'hôte distant. L'hôte distant ne peut probablement pas répondre avec l'ACK que le socket attend.
mirage
oui, c'est vrai (à partir du code du noyau). mais je doute aussi de la SEQ du paquet que vous envoyez, qui est "10", le noyau ne le vérifie-t-il pas?
Chinaxing
Probablement pas. Je pense que j'ai essayé avec de nombreux nombres aléatoires, et ils semblaient fonctionner.
mirage du
3

Il convient de mentionner que l' Socketinstance à la fois client et serveur doit être invoquée explicitement close(). Si une seule des extrémités appelle close()alors aussi, la socket restera dans l'état CLOSE_WAIT.

Binita Bharati
la source
3

Vous pouvez fermer de force les sockets avec la sscommande; la sscommande est un outil utilisé pour vider les statistiques de socket et affiche des informations de la même manière (bien que plus simple et plus rapide) à netstat.

Pour tuer n'importe quel socket dans l'état CLOSE_WAIT, exécutez ceci (en tant que root)

$ ss --tcp state CLOSE-WAIT --kill
Mustapha Hadid
la source
1

Il est également intéressant de noter que si votre programme génère un nouveau processus, ce processus peut hériter de tous vos descripteurs ouverts. Même après la fermeture de votre propre programme, ces handles hérités peuvent toujours être actifs via le processus enfant orphelin. Et ils n'apparaissent pas nécessairement tout à fait de la même manière dans netstat. Mais tout de même, le socket restera dans CLOSE_WAIT tant que ce processus enfant est actif.

J'ai eu un cas où je dirigeais ADB. ADB lui-même génère un processus serveur s'il n'est pas déjà en cours d'exécution. Cela a hérité de tous mes descripteurs au départ, mais ne s'est révélé en posséder aucun lorsque j'enquêtais (la même chose était vraie pour macOS et Windows - pas sûr pour Linux).

Ian
la source