133 votes

Comment fermer de force un socket en TIME_WAIT ?

Je lance un programme particulier sur Linux qui plante parfois. Si vous l'ouvrez rapidement après cela, il écoute sur le socket 49201 au lieu de 49200 comme il l'a fait la première fois. netstat révèle que 49200 est dans un état TIME_WAIT.

Y a-t-il un programme que vous pouvez exécuter pour forcer immédiatement ce socket à sortir de l'état TIME_WAIT ?

1 votes

Si vous êtes ici en raison de "trop de TIME_WAIT sur le serveur", il suffit de passer les trois premières réponses qui évitent la question au lieu d'y répondre.

0 votes

J'ai accepté ce qui était la 4e réponse, elle devrait donc être en haut maintenant

0 votes

166voto

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

Laissez-moi élaborer. Le protocole de contrôle de transmission (TCP) est conçu pour être un protocole de transmission de données bidirectionnel, ordonné et fiable entre deux points finaux (programmes). Dans ce contexte, le terme fiable signifie qu'il va retransmettre les paquets s'ils sont perdus en cours de route. TCP garantit la fiabilité en renvoyant des paquets d'accusé de réception (ACK) pour un seul paquet ou une plage de paquets reçus du pair.

Cela s'applique également aux signaux de contrôle tels que la demande/réponse de terminaison. RFC 793 définit l'état TIME-WAIT comme suit:

TIME-WAIT - représente l'attente du temps suffisant pour être certain que le TCP distant a reçu l'accusé de réception de sa demande de terminaison de connexion.

Consultez le schéma d'état TCP suivant: texte alternatif

TCP est un protocole de communication bidirectionnel, donc lorsque la connexion est établie, il n'y a pas de différence entre le client et le serveur. De plus, l'un ou l'autre peut décider de terminer la connexion, et les deux pairs doivent être d'accord pour la fermer complètement.

Appelons le premier à terminer la connexion comme le fermeur actif, et l'autre pair le fermeur passif. Lorsque le fermeur actif envoie FIN, l'état passe à FIN-WAIT-1. Ensuite, il reçoit un ACK pour le FIN envoyé et l'état passe à FIN-WAIT-2. Une fois qu'il reçoit également FIN du fermeur passif, le fermeur actif envoie l'ACK au FIN et l'état passe à TIME-WAIT. Dans le cas où le fermeur passif n'a pas reçu l'ACK pour le deuxième FIN, il retransmettra le paquet FIN.

RFC 793 fixe le TIMEOUT à deux fois la durée de vie maximale du segment, soit 2MSL. Puisque MSL, le temps maximum qu'un paquet peut errer sur Internet, est de 2 minutes, 2MSL équivaut à 4 minutes. Comme il n'y a pas d'ACK à un ACK, le fermeur actif ne peut rien faire d'autre que d'attendre 4 minutes s'il respecte correctement le protocole TCP/IP, au cas où le destinataire passif n'aurait pas reçu l'ACK pour son FIN (théoriquement).

En réalité, les paquets manquants sont probablement rares, et très rares s'ils se produisent tous à l'intérieur du LAN ou à l'intérieur d'une seule machine.

Pour répondre à la question littéralement, Comment fermer de force un socket en TIME_WAIT?, je vais quand même rester fidèle à ma réponse originale:

/etc/init.d/networking restart

En pratique, je le programmerais pour qu'il ignore l'état TIME-WAIT en utilisant l'option SO_REUSEADDR comme mentionné par WMR. Que fait exactement SO_REUSEADDR?

Cette option de socket indique au noyau que même si ce port est occupé (en
état TIME_WAIT), continuez et réutilisez-le quand même. Si c'est occupé, mais avec un autre état, vous obtiendrez toujours une erreur d'adresse déjà utilisée. C'est utile si votre serveur a été arrêté, et ensuite redémarré tout de suite alors que des sockets sont toujours actifs sur son port. Vous devez être conscient que si des données inattendues arrivent, cela peut confondre votre serveur, mais bien que cela soit possible, cela n'est pas probable.

12 votes

Grande réponse, mais pas la réponse correcte à sa question. Redémarrer le réseau fonctionnerait, mais aussi redémarrer, donc cela ne peut pas être correct.

4 votes

@Chris Huang-Leaver, la question est "Y a-t-il un programme que vous pouvez exécuter pour forcer immédiatement ce socket à sortir de l'état TIME_WAIT ?" si le redémarrage peut être considéré comme l'exécution d'un programme, alors cela serait aussi une bonne réponse. Pourquoi pensez-vous que ce ne peut pas être juste ?

9 votes

WRM a la réponse la plus utile (ce que je fais quand je rencontre ce genre de problème). Redémarrer le réseau est une solution trop radicale, et cela pourrait prendre plus de temps que d'attendre simplement le délai.La bonne réponse à sa question est "Non", mais SO ne vous permettra pas de taper des réponses de deux lettres :-)

54voto

WMR Points 861

Je ne sais pas si vous avez le code source de ce programme en particulier que vous exécutez, mais si c'est le cas, vous pouvez simplement définir SO_REUSEADDR via setsockopt(2) ce qui vous permet de vous lier à la même adresse locale même si le socket est dans l'état TIME_WAIT (à moins que ce socket n'écoute activement, voir socket(7)).

Pour plus d'informations sur l'état TIME_WAIT, consultez le FAQ sur les sockets Unix.

0 votes

Mais je n'ai pas reçu l'erreur déjà liée. lorsque j'exécute à nouveau le programme, il écoute en post(123456) aussi je peux voir que le système montre TIME_WAIT pour ce port mais je peux toujours me connecter. pourquoi?

3 votes

Même avec SO_REUSEADDR, il est encore possible de rencontrer l'erreur "Adresse déjà utilisée". Pour plus de détails, consultez hea-www.harvard.edu/~fine/Tech/addrinuse.html.

0 votes

@WMR La balise SO_REUSEADDR ne "ferme" pas un socket. Elle vous permet simplement de réutiliser ceux qui sont déjà ouverts. Donc la question demeure "Comment fermer de force un socket en TIME_WAIT ?"

36voto

Ben Hamill Points 655

À ma connaissance, il n'y a pas de moyen de fermer de force le socket en dehors d'écrire un meilleur gestionnaire de signal dans votre programme, mais il existe un fichier /proc qui contrôle la durée du délai d'attente. Le fichier est

/proc/sys/net/ipv4/tcp_tw_recycle

et vous pouvez définir le délai d'attente à 1 seconde en faisant ceci :

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

Cependant, cette page contient un avertissement concernant les problèmes de fiabilité possibles lors du réglage de cette variable.

Il existe également un fichier connexe

/proc/sys/net/ipv4/tcp_tw_reuse

qui contrôle si les sockets TIME_WAIT peuvent être réutilisés (probablement sans délai d'attente).

Par ailleurs, la documentation du noyau vous met en garde de ne pas modifier ces valeurs sans les 'conseils/demandes d'experts techniques'. Ce que je ne suis pas.

Le programme doit avoir été écrit pour essayer de lier au port 49200, puis d'incrémenter de 1 si le port est déjà utilisé. Par conséquent, si vous avez le contrôle du code source, vous pourriez modifier ce comportement pour attendre quelques secondes et réessayer sur le même port au lieu d'incrémenter.

0 votes

Je pense que les deux derniers exemples devraient être s/rw/tw/ que je modifierais, mais je n'ai pas assez de réputation.

1 votes

Pris dans la documentation du noyau : Attention. both tcp_tw_recycle and tcp_tw_reuse peuvent causer des problèmes. Vous ne devez pas activer l'un ou l'autre sans comprendre la topologie réseau entre les nœuds qui sont utilisés par ou qui utilisent le nœud où le paramètre est activé. Les connexions qui passent par des nœuds qui sont conscients des états de connexion TCP, tels que le pare-feu, le NAT ou l'équilibreur de charge, peuvent commencer à perdre des trames en raison du paramètre. Le problème deviendra visible lorsqu'il y a un nombre suffisamment élevé de connexions.

0 votes

En mettant 1 fonctionne pour les connexions futures, mais qu'en est-il de celles actuelles qui sont déjà ouvertes ?

21voto

akostadinov Points 1088

En fait, il y a un moyen de tuer une connexion - killcx. Ils prétendent que cela fonctionne à n'importe quel état de la connexion (ce que je n'ai pas vérifié). Vous devez connaître l'interface où se fait la communication cependant, il semble supposer eth0 par défaut.

MISE À JOUR: une autre solution est cutter qui est disponible dans les dépôts de certaines distributions Linux.

0 votes

Killcx ne fonctionne pas pour moi. Cutter nécessite un pare-feu pour interagir.

0 votes

Est-il possible d'exécuter une commande sur Redhat 7 ou une version ultérieure pour réaliser ceci ?

0 votes

@nitinr708 rien de prêt à l'emploi à ma connaissance :/ vous devez installer à partir des projets en amont.

3voto

Une autre option consiste à utiliser l'option SO_LINGER avec un délai d'attente de 0. De cette façon, lorsque vous fermez le socket, il est fermé de force, envoyant un RST au lieu d'entrer dans le comportement de fermeture FIN/ACK. Cela évitera l'état TIME_WAIT, et peut être plus approprié pour certaines utilisations.

3 votes

Il perd également toutes les données sortantes qui sont encore en transit, et peut causer une erreur à l'autre extrémité. Non recommandé.

0 votes

@EJP Échouer tôt est presque toujours le bon choix. Le réseau n'est pas fiable, et lutter contre cela ralentira les choses. Une application qui plante ne peut pas supposer que les données ont été transmises en toute sécurité.

2 votes

En fait, je recommanderais ceci n'importe quel jour où l'autre point final est une passerelle de bus industriel intégré boguée qui implémente son propre transport fiable de couche application sur TCP, où ledit transport empêche la connexion de se fermer à moins de recevoir RST et remplit donc la limite de connexion sur cette passerelle. Là. Je vous ai donné un exemple très spécifique et très réel qui, malheureusement, nécessite de recourir à des astuces comme celles-ci.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X