Je pense que je comprends le sens formel de l'option. Dans certains codes hérités que je gère actuellement, l'option est utilisée. Le client se plaint de RST en réponse à FIN de son côté sur la connexion à proximité de son côté.
Je ne suis pas sûr de pouvoir le supprimer en toute sécurité, car je ne comprends pas quand il doit être utilisé.
Pouvez-vous donner un exemple du moment où l'option serait requise?
Réponses:
La raison typique pour définir un
SO_LINGER
délai d'expiration de zéro est d'éviter un grand nombre de connexions dans leTIME_WAIT
état, bloquant toutes les ressources disponibles sur un serveur.Lorsqu'une connexion TCP est fermée proprement, la fin qui a initié la fermeture («fermeture active») se termine par la connexion
TIME_WAIT
pendant plusieurs minutes. Donc, si votre protocole est celui où le serveur initie la fermeture de la connexion et implique un très grand nombre de connexions de courte durée, il peut être sensible à ce problème.Ce n'est pas une bonne idée, cependant -
TIME_WAIT
existe pour une raison (pour s'assurer que les paquets parasites d'anciennes connexions n'interfèrent pas avec les nouvelles connexions). Il est préférable de reconcevoir votre protocole en un protocole dans lequel le client initie la fermeture de la connexion, si possible.la source
TIME_WAIT
volonté s'assiéra au client sans nuire. Rappelez-vous comme il est dit dans "UNIX Network Programming" troisième édition (Stevens et al) page 203: "L'état TIME_WAIT est votre ami et est là pour nous aider. Au lieu d'essayer d'éviter l'état, nous devons le comprendre (Section 2.7) . "Pour ma suggestion, veuillez lire la dernière section: "Quand utiliser SO_LINGER avec timeout 0" .
Avant d'en venir à cela, une petite conférence sur:
TIME_WAIT
FIN
,ACK
etRST
Terminaison TCP normale
La séquence de terminaison TCP normale ressemble à ceci (simplifié):
Nous avons deux pairs: A et B
close()
FIN
à BFIN_WAIT_1
étatFIN
ACK
à ACLOSE_WAIT
étatACK
FIN_WAIT_2
étatclose()
FIN
à ALAST_ACK
étatFIN
ACK
à BTIME_WAIT
étatACK
CLOSED
état - c'est-à-dire qu'il est supprimé des tables de socketTEMPS D'ATTENTE
Ainsi, le pair qui initie la terminaison - c'est-à-dire appelle en
close()
premier - se retrouvera dans l'TIME_WAIT
état.Pour comprendre pourquoi l'
TIME_WAIT
État est notre ami, veuillez lire la section 2.7 de la troisième édition de "UNIX Network Programming" de Stevens et al (page 43).Cependant, cela peut être un problème avec beaucoup de sockets dans
TIME_WAIT
état sur un serveur car cela pourrait éventuellement empêcher de nouvelles connexions d'être acceptées.Pour contourner ce problème, j'ai vu beaucoup suggérer de définir l'option de socket SO_LINGER avec timeout 0 avant d'appeler
close()
. Cependant, il s'agit d'une mauvaise solution car elle provoque l'arrêt de la connexion TCP avec une erreur.Au lieu de cela, concevez votre protocole d'application de sorte que la fin de la connexion soit toujours lancée du côté client. Si le client sait toujours quand il a lu toutes les données restantes, il peut lancer la séquence de terminaison. À titre d'exemple, un navigateur sait à partir de l'
Content-Length
en-tête HTTP quand il a lu toutes les données et peut lancer la fermeture. (Je sais que dans HTTP 1.1, il le gardera ouvert pendant un certain temps pour une éventuelle réutilisation, puis le fermera.)Si le serveur doit fermer la connexion, concevez le protocole d'application de sorte que le serveur demande au client d'appeler
close()
.Quand utiliser SO_LINGER avec timeout 0
Encore une fois, selon « réseau UNIX programmation » troisième édition , page 202-203, réglage
SO_LINGER
avec temporisation 0 avant d'appelerclose()
provoquera la séquence de terminaison normale ne doit être initié.Au lieu de cela, le pair définissant cette option et appelant
close()
enverra uneRST
(réinitialisation de la connexion) qui indique une condition d'erreur et c'est ainsi qu'elle sera perçue à l'autre extrémité. Vous verrez généralement des erreurs telles que «réinitialisation de la connexion par le pair».Par conséquent, dans la situation normale, c'est une très mauvaise idée de définir
SO_LINGER
avec timeout 0 avant d'appelerclose()
- à partir de maintenant appelé abortive close - dans une application serveur.Cependant, certaines situations le justifient quand même:
CLOSE_WAIT
ou de se retrouver dans l'TIME_WAIT
état.TIME_WAIT
(lors d'un appelclose()
depuis le serveur) car cela pourrait empêcher le serveur d'obtenir les ports disponibles pour les nouvelles connexions client après avoir été redémarré.CLOSE_WAIT
essayant de fournir des données à un terminal bloqué. port, mais réinitialiserait correctement le port bloqué s’il réussissaitRST
à supprimer les données en attente. »Je recommanderais ce long article qui, je crois, donne une très bonne réponse à votre question.
la source
TIME_WAIT
est un ami seulement quand il ne commence pas à causer des problèmes: stackoverflow.com/questions/1803566/…Lorsque l'attente est activée mais que le délai d'attente est nul, la pile TCP n'attend pas l'envoi des données en attente avant de fermer la connexion. Des données pourraient être perdues à cause de cela, mais en réglant Linger de cette façon, vous acceptez cela et demandez que la connexion soit réinitialisée immédiatement plutôt que fermée correctement. Cela provoque l'envoi d'un RST au lieu du FIN habituel.
Merci à EJP pour son commentaire, voir ici pour plus de détails.
la source
Le fait que vous puissiez supprimer la persistance de votre code en toute sécurité ou non dépend du type de votre application: est-ce un «client» (ouverture des connexions TCP et le fermer activement en premier) ou est-ce un «serveur» (écoute d'un TCP ouvert et le fermer après que l'autre côté a lancé la fermeture)?
Si votre application a la saveur d'un «client» (fermer en premier) ET que vous initiez et fermez un grand nombre de connexions à différents serveurs (par exemple lorsque votre application est une application de surveillance supervisant l'accessibilité d'un grand nombre de serveurs différents), votre application a le problème que toutes vos connexions client sont bloquées dans l'état TIME_WAIT. Ensuite, je recommanderais de raccourcir le délai d'expiration à une valeur plus petite que la valeur par défaut pour toujours s'arrêter correctement mais libérer les ressources de connexions client plus tôt. Je ne définirais pas le délai d'expiration sur 0, car 0 ne s'arrête pas correctement avec FIN mais abandonne avec RST.
Si votre application a la saveur d'un «client» et doit récupérer une énorme quantité de petits fichiers sur le même serveur, vous ne devez pas initier une nouvelle connexion TCP par fichier et vous retrouver dans une énorme quantité de connexions client dans TIME_WAIT, mais gardez la connexion ouverte et récupérez toutes les données sur la même connexion. L'option Linger peut et doit être supprimée.
Si votre application est un «serveur» (fermez la seconde en réaction à la fermeture du pair), à la fermeture (), votre connexion s'arrête normalement et les ressources sont libérées car vous n'entrez pas dans l'état TIME_WAIT. Linger ne doit pas être utilisé. Mais si votre application de serveur a un processus de supervision détectant les connexions ouvertes inactives inactives pendant une longue période («longue» doit être définie), vous pouvez arrêter cette connexion inactive de votre côté - voir cela comme une sorte de gestion des erreurs - avec un arrêt avorté. Cela se fait en définissant le délai d'attente à 0. close () enverra alors un RST au client, lui disant que vous êtes en colère :-)
la source
Dans les serveurs, vous souhaiterez peut-être envoyer
RST
au lieu deFIN
déconnecter des clients qui se comportent mal. Cela sauteFIN-WAIT
suivi desTIME-WAIT
états de socket dans le serveur, ce qui empêche d'épuiser les ressources du serveur et, par conséquent, protège de ce type d'attaque par déni de service.la source
J'aime l'observation de Maxim selon laquelle les attaques DOS peuvent épuiser les ressources du serveur. Cela se produit également sans adversaire réellement malveillant.
Certains serveurs doivent faire face à «l'attaque DOS involontaire» qui se produit lorsque l'application cliente a un bogue avec une fuite de connexion, où ils continuent de créer une nouvelle connexion pour chaque nouvelle commande qu'ils envoient à votre serveur. Et puis peut-être finir par fermer leurs connexions s'ils atteignent la pression du GC, ou peut-être que les connexions finissent par expirer.
Un autre scénario est celui où «tous les clients ont la même adresse TCP». Ensuite, les connexions client ne se distinguent que par les numéros de port (si elles se connectent à un seul serveur). Et si les clients commencent à cycler rapidement les connexions d'ouverture / fermeture pour une raison quelconque, ils peuvent épuiser l'espace de tuple (adresse client + port, IP serveur + port).
Je pense donc que les serveurs peuvent être mieux avisés de passer à la stratégie Linger-Zero lorsqu'ils voient un nombre élevé de sockets dans l'état TIME_WAIT - bien que cela ne corrige pas le comportement du client, cela pourrait réduire l'impact.
la source
Le socket d'écoute sur un serveur peut utiliser la fonction Linger avec le temps 0 pour accéder immédiatement à la liaison au socket et pour réinitialiser tous les clients dont les connexions ne sont pas encore terminées. TIME_WAIT est quelque chose qui n'est intéressant que lorsque vous avez un réseau à chemins multiples et que vous pouvez vous retrouver avec des paquets mal ordonnés ou si vous avez affaire à un ordre / à l'arrivée de paquets réseau impairs.
la source