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
- Un appel
close()
- A envoie
FIN
à B
- A va dans
FIN_WAIT_1
état
- B reçoit
FIN
- B envoie
ACK
à A
- B va dans
CLOSE_WAIT
état
- A reçoit
ACK
- A va dans
FIN_WAIT_2
état
- B appels
close()
- B envoie
FIN
à A
- B va dans
LAST_ACK
état
- A reçoit
FIN
- A envoie
ACK
à B
- A va dans
TIME_WAIT
état
- 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.