socket.shutdown vs socket.close

122

J'ai récemment vu un peu de code qui ressemblait à ceci (avec sock étant un objet socket bien sûr):

sock.shutdown(socket.SHUT_RDWR)
sock.close()

Quel est exactement le but d'appeler shutdown sur le socket puis de le fermer? Si cela fait une différence, ce socket est utilisé pour les E / S non bloquantes.

Jason Baker
la source

Réponses:

38

Voici une explication :

Une fois qu'un socket n'est plus nécessaire, le programme appelant peut supprimer le socket en appliquant un sous-programme de fermeture au descripteur de socket. Si un socket de livraison fiable est associé à des données lors de la fermeture, le système continue de tenter le transfert de données. Cependant, si les données ne sont toujours pas livrées, le système les ignore. Si le programme d'application n'a aucune utilisation pour les données en attente, il peut utiliser le sous-programme d'arrêt sur le socket avant de le fermer.

Bob Nadler
la source
241

L'appel closeet shutdownont deux effets différents sur le socket sous-jacent.

La première chose à souligner est que le socket est une ressource dans le système d'exploitation sous-jacent et que plusieurs processus peuvent avoir un handle pour le même socket sous-jacent.

Lorsque vous l'appelez closedécrémente le nombre de descripteurs de un et si le nombre de descripteurs a atteint zéro, le socket et la connexion associée passent par la procédure de fermeture normale (en envoyant effectivement un FIN / EOF à l'homologue) et le socket est désalloué.

Ce à quoi il faut faire attention ici est que si le nombre de descripteurs n'atteint pas zéro parce qu'un autre processus a encore un descripteur sur le socket, la connexion n'est pas fermée et le socket n'est pas désalloué.

D'autre part, l'appel shutdownà la lecture et à l'écriture ferme la connexion sous-jacente et envoie un FIN / EOF à l'homologue quel que soit le nombre de processus qui ont des descripteurs sur le socket. Cependant, il ne désalloue pas le socket et vous devez toujours appeler close par la suite.

Robert S. Barnes
la source
bonne réponse, je n'ai jamais pris la peine de savoir ce que shutdown()fait :)
Matt Joiner
2
Un shutdown () pour lecture peut-il provoquer l'envoi d'un paquet FIN? c'est-à-dire arrêt (sock_fd, 1);
ernesto le
Serait-il judicieux de toujours appeler .shutdown()et sur la ligne suivante .close()? Ou devrait-il y avoir un délai entre les deux?
Luc
@Luc dépend complètement de ce que vous faites.
Robert
2
@Luc Ensuite, il suffit de fermer, tant qu'aucun autre processus n'a de handle vers le socket.
Robert
17

Explication de l'arrêt et de la fermeture: arrêt progressif (msdn)

Shutdown (dans votre cas) indique à l'autre extrémité de la connexion qu'il n'y a plus aucune intention de lire ou d'écrire sur le socket. Puis close libère toute mémoire associée au socket.

L'omission de l'arrêt peut entraîner la persistance du socket dans la pile des systèmes d'exploitation jusqu'à ce que la connexion ait été fermée correctement.

OMI les noms «arrêt» et «fermer» sont trompeurs, «fermer» et «détruire» souligneraient leurs différences.

Dale Reidy
la source
Cela n'indique pas à l'autre extrémité qu'il n'y a plus aucune intention de lire. L'arrêt pour lecture n'envoie rien au pair.
Marquis of Lorne
7

il est mentionné directement dans le Socket Programming HOWTO ( py2 / py3 )

Déconnexion

À proprement parler, vous êtes censé l'utiliser shutdownsur une prise avant de vous close. Le shutdownest un avertissement à la prise à l'autre extrémité. Selon l'argument que vous lui transmettez, cela peut signifier « Je ne vais plus envoyer, mais je vais quand même écouter », ou « Je n'écoute pas, bon débarras! ». La plupart des bibliothèques de sockets, cependant, sont tellement habituées aux programmeurs qui négligent d'utiliser cette étiquette que normalement a closeest le même que shutdown(); close(). Ainsi, dans la plupart des situations, un arrêt explicite n'est pas nécessaire.

...

mykhal
la source
Cette information n'est pas correcte. Il n'est jamais nécessaire d'arrêter une socket pour l'écriture si (1) vous avez bifurqué le processus et que vous voulez vraiment envoyer le FIN maintenant, ou (2) vous vous engagez dans un protocole de lecture mutuelle vers EOS tel que les deux pairs se ferment en même temps. Sinon, close()c'est suffisant. La documentation Python doit être corrigée.
Marquis de Lorne
4

Ce code ci-dessus n'est-il pas faux?

L'appel de fermeture directement après l'appel d'arrêt peut faire en sorte que le noyau rejette de toute façon tous les tampons sortants.

Selon http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable, il faut attendre entre l'arrêt et le close jusqu'à ce que read renvoie 0.

Christian
la source
Pas correcte. Le noyau ne supprimera les tampons sortants que si la connexion a été réinitialisée, ce qui peut se produire si l'application locale n'a pas lu toutes les données entrantes en attente déjà arrivées, ou si le pair a joué avec SO_LINGER, ce qu'il ne devrait pas faire . Il n'est pas non plus nécessaire de dormir, ni même d'appeler l'arrêt avant la fermeture. Il y a beaucoup de désinformation sur cette question.
Marquis de Lorne
1

Shutdown (1), force le socket no à envoyer plus de données

Ceci est utile dans

1- Rinçage du tampon

2- Détection d'erreur étrange

3- Protection sûre

Permettez-moi de vous expliquer plus, lorsque vous envoyez une donnée de A à B, il n'est pas garanti qu'elle soit envoyée à B, elle est uniquement garantie d'être envoyée au tampon A os, qui à son tour l'envoie au tampon B os

Donc en appelant shutdown (1) sur A, vous videz le tampon de A et une erreur est levée si le tampon n'est pas vide c'est-à-dire: les données n'ont pas encore été envoyées au pair

Cependant, cela est irréversible, vous pouvez donc le faire après avoir complètement envoyé toutes vos données et vous voulez être sûr que c'est au moins dans le tampon du système d'exploitation du pair.

utilisateur306166
la source
L'arrêt ne force pas un rinçage. La situation de mise en mémoire tampon est inchangée. Et si le tampon n'est pas vide, aucune erreur n'est générée. Le FIN est simplement mis en file d'attente derrière les données en attente. La réponse est complètement incorrecte.
Marquis de Lorne