123 votes

Option TCP SO_LINGER (zéro) - quand c'est nécessaire

Je pense que je comprends la signification formelle de l'option. L'option est utilisée dans un code hérité dont je m'occupe actuellement. Le client se plaint de RST comme réponse à FIN de son côté lors de la fermeture de la connexion 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 de cas où cette option serait nécessaire ?

247voto

mgd Points 713

Pour ma suggestion, veuillez lire la dernière section : "Quand utiliser SO_LINGER avec timeout 0" .

Avant d'en arriver là, un petit cours sur.. :

  • Terminaison normale du TCP
  • TIME_WAIT
  • FIN , ACK et RST

Terminaison normale du TCP

La séquence normale de terminaison du TCP ressemble à ceci (simplifié) :

Nous avons deux pairs : A et B

  1. Un appel close()
    • A envoie FIN à B
    • A va dans FIN_WAIT_1 état
  2. B reçoit FIN
    • B envoie ACK à A
    • B va dans CLOSE_WAIT état
  3. A reçoit ACK
    • A va dans FIN_WAIT_2 état
  4. B appels close()
    • B envoie FIN à A
    • B va dans LAST_ACK état
  5. A reçoit FIN
    • A envoie ACK à B
    • A va dans TIME_WAIT état
  6. B reçoit ACK
    • B va à CLOSED état - c'est-à-dire qu'il est retiré des tables de socket

TIME_WAIT

Ainsi, le pair qui initie la résiliation - c'est-à-dire qui appelle close() d'abord - se retrouvera dans le TIME_WAIT l'État.

Pour comprendre pourquoi le TIME_WAIT L'état est notre ami, veuillez lire la section 2.7 de "UNIX Network Programming" troisième édition par Stevens et al (page 43).

Cependant, cela peut être un problème avec beaucoup de prises en TIME_WAIT sur un serveur, car il pourrait éventuellement empêcher l'acceptation de nouvelles connexions.

Pour contourner ce problème, j'ai vu beaucoup de personnes suggérer de définir l'option de socket SO_LINGER avec le timeout 0 avant d'appeler close() . Cependant, il s'agit d'une mauvaise solution car elle entraîne la fin de la connexion TCP avec une erreur.

Concevez plutôt votre protocole d'application de manière à ce que la fin de la connexion soit toujours initiée du côté client. Si le client sait toujours quand il a lu toutes les données restantes, il peut initier la séquence de fin de connexion. Par exemple, un navigateur sait depuis le Content-Length HTTP lorsqu'il a lu toutes les données et peut lancer la fermeture. (Je sais qu'en HTTP 1.1, il le garde ouvert pendant un certain temps pour une éventuelle réutilisation, puis le ferme).

Si le serveur doit fermer la connexion, concevez le protocole d'application de manière à ce que le serveur demande au client d'appeler close() .

Quand utiliser SO_LINGER avec timeout 0

Encore une fois, d'après "UNIX Network Programming", troisième édition, pages 202-203, il faut définir SO_LINGER avec le délai d'attente 0 avant d'appeler close() provoquera la séquence de terminaison normale pas pour être initié.

Au lieu de cela, le pair définit cette option et appelle close() enverra un RST (réinitialisation de la connexion) qui indique une condition d'erreur et c'est ainsi qu'il sera perçu à l'autre extrémité. Vous verrez généralement des erreurs telles que "Connection reset by peer".

Par conséquent, dans une situation normale, c'est une très mauvaise idée de définir SO_LINGER avec le délai d'attente 0 avant d'appeler close() - désormais appelé fermeture abortive - dans une application serveur.

Cependant, certaines situations justifient de le faire quand même :

  • Si le client de votre application serveur se comporte mal (dépassement de délai, retour de données non valides, etc.), il est judicieux de procéder à une fermeture avortée pour éviter d'être bloqué en CLOSE_WAIT ou de se retrouver dans le TIME_WAIT l'État.
  • Si vous devez redémarrer votre application serveur qui a actuellement des milliers de connexions client, vous pouvez envisager de définir cette option de socket pour éviter des milliers de sockets serveur dans TIME_WAIT (lors de l'appel close() du côté du serveur) car cela pourrait empêcher le serveur d'obtenir des ports disponibles pour les nouvelles connexions des clients après le redémarrage.
  • À la page 202 du livre susmentionné, il est spécifiquement indiqué : "Certaines circonstances justifient l'utilisation de cette fonction pour envoyer une fermeture avortée. Un exemple est un serveur de terminal RS-232, qui peut rester bloqué indéfiniment en mode CLOSE_WAIT essayant de délivrer des données à un port de terminal bloqué, mais réinitialiserait correctement le port bloqué s'il obtenait une RST pour rejeter les données en attente."

Je recommande ce long article qui, selon moi, répond très bien à votre question.

99voto

caf Points 114951

La raison typique de définir un SO_LINGER Le délai d'attente de zéro a pour but d'éviter qu'un grand nombre de connexions restent en attente dans le système. TIME_WAIT ce qui mobilise toutes les ressources disponibles sur un serveur.

Lorsqu'une connexion TCP est fermée proprement, l'extrémité qui a initié la fermeture ("fermeture active") se retrouve avec la connexion dans le dossier de l'utilisateur. 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, alors il pourrait ê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 errants des anciennes connexions n'interfèrent pas avec les nouvelles connexions). Il est préférable de revoir la conception de votre protocole pour que ce soit le client qui initie la fermeture de la connexion, si possible.

18voto

Len Holgate Points 12579

Lorsque la fonction "linger" est activée mais que le délai d'attente est égal à zéro, la pile TCP n'attend pas que les données en attente soient envoyées avant de fermer la connexion. Des données peuvent être perdues à cause de cela, mais en paramétrant linger de cette façon, vous acceptez cela et demandez que la connexion soit réinitialisée immédiatement plutôt que d'être fermée gracieusement. Cela entraîne l'envoi d'un RST au lieu de la FIN habituelle.

Merci à EJP pour son commentaire, voir ici pour les détails.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X