Question Comment fermer de force un socket dans 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.

Existe-t-il un programme que vous pouvez exécuter pour forcer immédiatement ce socket à quitter l'état TIME_WAIT?


109
2017-09-03 12:57


origine


Si vous êtes ici à cause de "trop TIME_WAIT sur le serveur ", juste passer à travers les trois premières réponses qui évitent la question au lieu d'y répondre. - Pacerier


Réponses:


/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 d'extrémité (programmes). Dans ce contexte, le terme fiable signifie qu'il retransmettra les paquets s'il se perd au milieu. Le protocole 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 de l'homologue.

Ceci est identique pour les signaux de contrôle tels que demande / réponse de terminaison. RFC 793 définit l'état TIME-WAIT comme suit:

TIME-WAIT - représente l'attente de   assez de temps pour passer pour être sûr       le TCP distant a reçu l'accusé de réception de sa connexion       demande de résiliation.

Voir le diagramme d'état TCP suivant: alt text

TCP est un protocole de communication bidirectionnel. Ainsi, 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 appeler à quitter, et les deux homologues doivent se mettre d'accord sur la fermeture pour fermer complètement une connexion TCP établie.

Appelons le premier à appeler les quittes en tant que rapproché actif, et l'autre à scruter le plus proche passif. Lorsque le système de fermeture 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 système de rapprochement passif, le mécanisme de rapprochement actif envoie l'ACK au système FIN et l'état passe à TIME-WAIT. Si le système de rapprochement passif n'a pas reçu l'ACK du second FIN, il retransmettra le paquet FIN.

RFC 793 définit TIME-OUT sur deux fois la durée maximale du segment, ou 2MSL. Depuis MSL, la durée maximale pendant laquelle un paquet peut errer sur Internet est définie sur 2 minutes, 2MSL sur 4 minutes. Étant donné qu’il n’existe pas d’accusé de réception, le système de fermeture actif ne peut rien faire, mais attendre 4 minutes s’il adhère correctement au protocole TCP / IP, au cas où l’expéditeur passif n’aurait pas reçu l’accusé de réception (FIN) (théoriquement). .

En réalité, les paquets manquants sont probablement rares, et très rares si tout se passe sur le réseau local ou sur un seul ordinateur.

Pour répondre à la question, comment forcer fermer une socket dans TIME_WAIT ?, je vais quand même m'en tenir à ma réponse d'origine:

/etc/init.d/networking restart

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

Cette option de socket dit au noyau   que même si ce port est occupé (en
  l'état TIME_WAIT), continuez et   le réutiliser quand même. Si c'est occupé, mais   avec un autre état, vous aurez toujours   une adresse déjà utilisée en erreur. Il   est utile si votre serveur a été fermé   vers le bas, puis redémarré immédiatement   tandis que les sockets sont toujours actifs sur son   Port. Vous devriez être conscient que si   toute donnée inattendue arrive, il peut   confondez votre serveur, mais alors que cela   est possible, ce n'est pas probable.


139
2017-09-03 13:11



Excellente réponse, mais pas la réponse correcte à sa question. Le redémarrage de la mise en réseau fonctionnerait, mais il en irait de même pour le redémarrage, de sorte que cela ne peut pas être correct. - Chris Huang-Leaver
@Chris Huang-Leaver, la question est "Existe-t-il un programme que vous pouvez exécuter pour forcer immédiatement ce socket à quitter l'état TIME_WAIT?" Si le redémarrage pouvait être considéré comme l'exécution d'un programme, alors ce serait aussi une bonne réponse. Pourquoi pensez-vous que cela ne peut pas être juste? - Eugene Yokota
WMR 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 trop radical pour être une solution et peut prendre plus de temps que simplement attendre le délai. La réponse correcte à sa question est «Non», mais SO ne vous laissera pas taper les réponses à deux lettres :-) - Chris Huang-Leaver
oh ok, la prochaine fois qu’un processus s’arrête sur SIGTERM, je vais simplement casser mon ordinateur au lieu de le réparer. - Longpoke


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

Pour plus d’informations sur l’état TIME_WAIT, voir la FAQ sur les sockets Unix.


50
2017-09-03 13:17



mais je n'ai pas eu l'erreur déjà liée. Quand j'exécute à nouveau le programme, il écoute en post (123456). Je peux également voir que le système affiche TIME_WAIT pour ce port, mais je peux quand même me connecter. Pourquoi? - Jayapal Chandran
Même avec SO_REUSEADDR, il est toujours possible d’obtenir l’erreur "Adresse déjà utilisée". Pour plus de détails, reportez-vous à www.harvard.edu/~fine/Tech/addrinuse.html. - Jingguo Yao
@WMR SO_REUSEADDR ne "ferme" pas une prise. Cela vous permet simplement de réutiliser ceux qui sont déjà ouverts. Donc, la question est toujours "Comment fermer de force un socket dans TIME_WAIT? " - Pacerier


Autant que je sache, il est impossible de fermer de force le socket en dehors de l'écriture d'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 procédant comme suit:

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

cependant, cette page contient un avertissement sur les problèmes de fiabilité possibles lors de la définition de cette variable.

Il y a aussi un fichier lié

/proc/sys/net/ipv4/tcp_tw_reuse

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

Incidemment, la documentation du noyau vous avertit de ne changer aucune de ces valeurs sans «conseils / demandes d'experts techniques». Ce que je ne suis pas.

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


32
2017-09-03 13:24



pense que les deux autres exemples devraient être s / rw / tw / je voudrais éditer, mais manque assez de rep.
Extrait de la documentation du noyau: Attention. Tcp_tw_recycle et tcp_tw_reuse peuvent provoquer des problèmes. Vous ne devez pas activer non plus sans comprendre la topologie du réseau entre les noeuds qui utilisent ou sont utilisés par le noeud sur lequel le paramètre est activé. Les connexions qui passent par des nœuds connaissant les états de connexion TCP, tels que le pare-feu, le NAT ou l'équilibreur de charge peuvent commencer à supprimer des trames en raison de ce paramètre. Le problème devient visible lorsque le nombre de connexions est suffisant.
Le mettre à 1 fonctionne pour les connexions futures, mais qu'en est-il de celles qui sont déjà ouvertes? - Pacerier


En fait, il existe un moyen de tuer une connexion - killcx. Ils prétendent que cela fonctionne dans n’importe quel état de la connexion (que je n’ai pas vérifié). Vous devez connaître l'interface où la communication a lieu, mais il semble que eth0 soit utilisé par défaut.

UPDATE: une autre solution est coupeur qui vient dans les dépôts de certaines distributions linux.


16
2017-10-30 17:32



Merci! Cet utilitaire fonctionne très bien! M'a sauvé de devoir redémarrer un long travail. - Zanson


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


3
2018-06-10 22:33



Il perd également toutes les données sortantes encore en transit et peut provoquer une erreur à l'autre extrémité. Non recommandé. - user207421
@EJP Un échec précoce est presque toujours le bon appel. La mise en réseau n'est pas fiable et les combats qui ralentiront les choses. Une application bloquée ne peut pas supposer que des données ont été enregistrées en toute sécurité. - Tobu
En fait, je le recommanderais tous les jours lorsque l'autre point d'extrémité est une passerelle de bus industrielle embarquée, buggée, qui implémente son propre transport fiable sur la couche d'application sur TCP, ledit transport empêchant la connexion de se fermer à moins qu'il ne reçoive la taxe RST et se remplit ainsi. 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 le recours à des piratages comme celui-ci. - andyn
@Tobu La mise en réseau n'est pas fiable, mais TCP essaie de l'être, et aggraver la situation ne signifie rien de mieux que de le faire, et laisser TCP faire son travail ne constitue pas une «lutte». - user207421


Une autre solution serait de disposer d’un logiciel de transfert de proxy ou de port fiable qui écoute sur le port 49200, puis de transférer la connexion vers l’une des instances de votre programme moins fiable utilisant différents ports ... HAPROXY me vient à l’esprit.

Incidemment, le port sur lequel vous vous connectez est assez élevé. Vous pouvez essayer d’utiliser un inutilisé juste au-dessus de la plage 0-1024. Votre système est moins susceptible d’utiliser un numéro de port inférieur en tant que port éphémère.


2
2017-08-21 20:28





TIME_WAIT est le problème le plus courant dans l’architecture serveur serveur de programmation par socket. Attendre quelques secondes en essayant périodiquement est la meilleure solution. Pour les applications en temps réel, ils ont besoin d'un serveur qui doit se lever immédiatement Il existe une option SO_REUSEADDR pour eux.


0
2017-10-13 19:07