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:
- Terminaison TCP normale
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
- A appelle
close()
- A envoie
FIN
à B
- A entre en
FIN_WAIT_1
état
- B reçoit
FIN
- B envoie
ACK
à A
- B entre dans l'
CLOSE_WAIT
état
- A reçoit
ACK
- A entre en
FIN_WAIT_2
état
- Appels B
close()
- B envoie
FIN
à A
- B entre dans l'
LAST_ACK
état
- A reçoit
FIN
- A envoie
ACK
à B
- A entre en
TIME_WAIT
état
- B reçoit
ACK
- B passe à l'
CLOSED
état - c'est-à-dire qu'il est supprimé des tables de socket
TEMPS 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'appeler close()
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 une RST
(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'appeler close()
- à partir de maintenant appelé abortive close - dans une application serveur.
Cependant, certaines situations le justifient quand même:
- Si le client de votre application serveur se comporte mal (expire, renvoie des données non valides, etc.), une fermeture avortée a du sens pour éviter d'être bloqué
CLOSE_WAIT
ou de se retrouver dans l' TIME_WAIT
é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
TIME_WAIT
(lors d'un appel close()
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é.
- À la page 202 du livre susmentionné, il est spécifiquement indiqué: "Il existe certaines circonstances qui justifient l'utilisation de cette fonctionnalité pour envoyer une fermeture avortée. Un exemple est un serveur de terminal RS-232, qui peut se bloquer indéfiniment en
CLOSE_WAIT
essayant de fournir des données à un terminal bloqué. port, mais réinitialiserait correctement le port bloqué s’il réussissait RST
à supprimer les données en attente. »
Je recommanderais ce long article qui, je crois, donne une très bonne réponse à votre question.