socket.shutdown vs socket.close

122

Ich habe kürzlich ein bisschen Code gesehen, der so aussah (wobei Socke natürlich ein Socket-Objekt ist):

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

Was genau ist der Zweck, den Socket herunterzufahren und dann zu schließen? Wenn es einen Unterschied macht, wird dieser Socket für nicht blockierende E / A verwendet.

Jason Baker
quelle

Antworten:

38

Hier ist eine Erklärung :

Sobald ein Socket nicht mehr benötigt wird, kann das aufrufende Programm den Socket verwerfen, indem es eine enge Unterroutine auf den Socket-Deskriptor anwendet. Wenn einem zuverlässigen Zustellungssockel beim Schließen Daten zugeordnet sind, versucht das System weiterhin, Daten zu übertragen. Wenn die Daten jedoch immer noch nicht zugestellt werden, verwirft das System die Daten. Sollte das Anwendungsprogramm keine ausstehenden Daten verwenden, kann es vor dem Schließen die Subroutine zum Herunterfahren auf dem Socket verwenden.

Bob Nadler
quelle
241

Aufruf closeund shutdownhaben zwei unterschiedliche Auswirkungen auf den zugrunde liegenden Socket.

Als erstes muss darauf hingewiesen werden, dass der Socket eine Ressource im zugrunde liegenden Betriebssystem ist und mehrere Prozesse ein Handle für denselben zugrunde liegenden Socket haben können.

Wenn Sie es aufrufen close, wird die Anzahl der Handles um eins verringert. Wenn die Anzahl der Handles Null erreicht hat, durchlaufen der Socket und die zugehörige Verbindung den normalen Schließvorgang (effektiv wird ein FIN / EOF an den Peer gesendet), und der Socket wird freigegeben.

Hierbei ist zu beachten, dass die Verbindung nicht geschlossen und der Socket nicht freigegeben wird, wenn die Anzahl der Handles nicht Null erreicht, da ein anderer Prozess noch ein Handle für den Socket hat .

Wenn Sie shutdownzum Lesen und Schreiben aufrufen, wird die zugrunde liegende Verbindung geschlossen und ein FIN / EOF an den Peer gesendet, unabhängig davon, wie viele Prozesse Handles für den Socket haben. Der Socket wird jedoch nicht freigegeben, und Sie müssen anschließend noch schließen.

Robert S. Barnes
quelle
tolle Antwort, ich habe shutdown()mich nie darum gekümmert herauszufinden, was macht :)
Matt Joiner
2
Kann ein shutdown () zum Lesen dazu führen, dass ein FIN-Paket gesendet wird? dh Herunterfahren (sock_fd, 1);
ernesto
Wäre es klug, immer anzurufen .shutdown()und in der nächsten Leitung zu sein .close()? Oder sollte es dazwischen eine Verzögerung geben?
Luc
@ Luc Hängt ganz davon ab, was du tust.
Robert S. Barnes
2
@Luc Dann ist einfach schließen in Ordnung, solange keine anderen Prozesse ein Handle für den Socket haben.
Robert S. Barnes
17

Erklärung zum Herunterfahren und Schließen: Graceful Shutdown (msdn)

Das Herunterfahren (in Ihrem Fall) zeigt an, dass am anderen Ende der Verbindung keine weitere Absicht besteht, vom Socket zu lesen oder darauf zu schreiben. Durch Schließen wird der mit dem Socket verknüpfte Speicher freigegeben.

Das Weglassen des Herunterfahrens kann dazu führen, dass der Socket im Betriebssystemstapel verbleibt, bis die Verbindung ordnungsgemäß geschlossen wurde.

IMO sind die Namen "Herunterfahren" und "Schließen" irreführend, "Schließen" und "Zerstören" würden ihre Unterschiede betonen.

Dale Reidy
quelle
Es zeigt dem anderen Ende nicht an, dass keine weitere Absicht zum Lesen besteht. Beim Herunterfahren zum Lesen wird nichts an den Peer gesendet.
Marquis von Lorne
7

es wird direkt im Socket Programming HOWTO ( py2 / py3 ) erwähnt

Trennen

Genau genommen sollten Sie shutdowneine Steckdose vor sich verwenden close. Dies shutdownist ein Hinweis für die Steckdose am anderen Ende. Abhängig von dem Argument, das Sie weitergeben, kann dies bedeuten: " Ich werde nicht mehr senden, aber ich werde trotzdem zuhören " oder " Ich höre nicht zu, gute Befreiung!" ”. Die meisten Socket-Bibliotheken sind jedoch so an Programmierer gewöhnt, die diese Etikette nicht verwenden, dass normalerweise a closedasselbe ist wie shutdown(); close(). In den meisten Situationen ist ein explizites Herunterfahren nicht erforderlich.

...

Mykhal
quelle
Diese Informationen sind nicht korrekt. Es ist immer nur erforderlich, einen Socket zum Schreiben herunterzufahren, wenn (1) Sie den Prozess verzweigt haben und definitiv jetzt die FIN senden möchten oder (2) Sie sich auf ein gegenseitiges Read-to-EOS-Protokoll einlassen, sodass beide Peers schließen gleichzeitig. Ansonsten close()ist ausreichend. Die Python-Dokumentation sollte korrigiert werden.
Marquis von Lorne
4

Ist dieser Code oben nicht falsch?

Der Aufruf zum Schließen direkt nach dem Aufruf zum Herunterfahren kann dazu führen, dass der Kernel ohnehin alle ausgehenden Puffer verwirft.

Laut http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable muss man zwischen dem Herunterfahren und dem warten Schließen, bis read 0 zurückgibt.

Christian
quelle
Nicht richtig. Der Kernel verwirft ausgehende Puffer nur, wenn die Verbindung zurückgesetzt wurde. Dies kann passieren, wenn die lokale App nicht alle ausstehenden eingehenden Daten gelesen hat, die bereits eingetroffen sind, oder wenn der Peer mit SO_LINGER in Konflikt geraten ist, was sie nicht tun sollten . Es besteht weder die Notwendigkeit zu schlafen, noch das Herunterfahren vor dem Schließen aufzurufen. Es gibt viele Fehlinformationen zu diesem Thema.
Marquis von Lorne
1

Beim Herunterfahren (1) wird der Socket no gezwungen, weitere Daten zu senden

Dies ist nützlich in

1- Pufferspülung

2- Seltsame Fehlererkennung

3- Sichere Bewachung

Lassen Sie mich näher erläutern: Wenn Sie Daten von A nach B senden, wird nicht garantiert, dass sie an B gesendet werden, sondern nur an den A os-Puffer, der sie wiederum an den B os-Puffer sendet

Wenn Sie also shutdown (1) für A aufrufen, leeren Sie den Puffer von A und es wird ein Fehler ausgegeben, wenn der Puffer nicht leer ist, dh: Daten wurden noch nicht an den Peer gesendet

Dies ist jedoch irreversibel. Sie können dies tun, nachdem Sie alle Ihre Daten vollständig gesendet haben und sicher sein möchten, dass sie mindestens im Peer-OS-Puffer gespeichert sind

user306166
quelle
Das Herunterfahren erzwingt keine Spülung. Die Puffersituation bleibt unverändert. Und wenn der Puffer nicht leer ist, wird kein Fehler ausgelöst. Die FIN wird lediglich hinter den ausstehenden Daten in die Warteschlange gestellt. Die Antwort ist völlig falsch.
Marquis von Lorne