it-swarm.com.de

TCP Option SO_LINGER (Null) - wenn es erforderlich ist

Ich denke, ich verstehe die formale Bedeutung der Option. In einigen älteren Codes, die ich gerade bearbeite, wird die Option verwendet. Der Kunde beschwert sich über RST als Reaktion auf FIN von seiner Seite bei einer Verbindung von seiner Seite.

Ich bin nicht sicher, ob ich es sicher entfernen kann, da ich nicht verstehe, wann es verwendet werden sollte.

Können Sie bitte ein Beispiel geben, wann die Option erforderlich wäre?

83
dimba

Der typische Grund für das Setzen eines SO_LINGER Timeout von Null ist, um zu vermeiden, dass eine große Anzahl von Verbindungen im TIME_WAIT state, bindet alle verfügbaren Ressourcen auf einem Server.

Wenn eine TCP Verbindung sauber geschlossen wird, endet das Ende, das das Schließen eingeleitet hat ("aktives Schließen"), mit der Verbindung in TIME_WAIT für einige Minuten. Wenn es sich bei Ihrem Protokoll also um ein Protokoll handelt, bei dem der Server den Verbindungsabbau initiiert und sehr viele kurzlebige Verbindungen umfasst, kann dieses Problem auftreten.

Dies ist jedoch keine gute Idee - TIME_WAIT existiert aus einem bestimmten Grund (um sicherzustellen, dass Streupakete von alten Verbindungen neue Verbindungen nicht stören). Es ist eine bessere Idee, Ihr Protokoll auf ein Protokoll umzugestalten, bei dem der Client den Verbindungsabschluss initiiert, sofern dies möglich ist.

71
caf

Lesen Sie für meinen Vorschlag bitte den letzten Abschnitt: „Wann soll SO_LINGER mit Timeout 0 verwendet werden?“ .

Bevor wir dazu kommen, ein kleiner Vortrag über:

  • Normal TCP Beendigung
  • TIME_WAIT
  • FIN, ACK und RST

Normal TCP Beendigung

Die normale TCP Terminierungssequenz sieht wie folgt aus (vereinfacht):

Wir haben zwei Kollegen: A und B

  1. A ruft close() Auf
    • A sendet FIN an B
    • A geht in den Zustand FIN_WAIT_1
  2. B erhält FIN
    • B sendet ACK an A
    • B geht in den Zustand CLOSE_WAIT
  3. A erhält ACK
    • A geht in den Zustand FIN_WAIT_2
  4. B ruft close() Auf
    • B sendet FIN an A
    • B geht in den Zustand LAST_ACK
  5. A erhält FIN
    • A sendet ACK an B
    • A geht in den Zustand TIME_WAIT
  6. B erhält ACK
    • B geht in den Zustand CLOSED - d. H. Wird aus den Socket-Tabellen entfernt

ZEIT WARTET

Der Peer, der die Beendigung initiiert - d. H. Zuerst close() aufruft - wird also in den Zustand TIME_WAIT versetzt.

Um zu verstehen, warum der TIME_WAIT -Status unser Freund ist, lesen Sie bitte Abschnitt 2.7 in der dritten Ausgabe von "UNIX Network Programming" von Stevens et al. (Seite 43).

Es kann jedoch ein Problem mit vielen Sockets im Status TIME_WAIT auf einem Server sein, da dies möglicherweise die Annahme neuer Verbindungen verhindert.

Um dieses Problem zu umgehen, haben viele vorgeschlagen, die SO_LINGER-Socket-Option auf Timeout 0 zu setzen, bevor close() aufgerufen wurde. Dies ist jedoch eine schlechte Lösung, da die Verbindung TCP mit einem Fehler beendet wird.

Entwerfen Sie stattdessen Ihr Anwendungsprotokoll so, dass der Verbindungsabbau immer vom Client aus initiiert wird. Wenn der Client immer weiß, wann er alle verbleibenden Daten gelesen hat, kann er die Beendigungssequenz einleiten. Beispielsweise weiß ein Browser anhand des HTTP-Headers Content-Length, wann er alle Daten gelesen hat, und kann das Schließen einleiten. (Ich weiß, dass es in HTTP 1.1 für eine Weile geöffnet bleibt, damit es möglicherweise wiederverwendet werden kann, und schließe es dann.)

Wenn der Server die Verbindung trennen muss, entwerfen Sie das Anwendungsprotokoll so, dass der Server den Client auffordert, close() aufzurufen.

Wann soll SO_LINGER mit Timeout 0 verwendet werden?

Erneut führt das Setzen von SO_LINGER mit der Zeitüberschreitung 0 vor dem Aufrufen von close() gemäß der dritten Ausgabe von "UNIX Network Programming" auf Seite 202-203 zu der normalen Beendigungssequenz not eingeleitet werden.

Stattdessen sendet der Peer, der diese Option einstellt und close() aufruft, ein RST (Verbindungszurücksetzung), das auf einen Fehlerzustand hinweist und auf diese Weise am anderen Ende wahrgenommen wird. In der Regel werden Fehler wie "Verbindung von Peer zurückgesetzt" angezeigt.

Daher ist es im Normalfall eine schlechte Idee, SO_LINGER vor dem Aufruf von close() auf 0 zu setzen - von nun an abortive close - In einer Serveranwendung.

Bestimmte Situationen rechtfertigen dies jedoch trotzdem:

  • Wenn sich ein Client Ihrer Serveranwendung schlecht verhält (Zeitüberschreitung, Rückgabe ungültiger Daten usw.), ist ein Abbruch des Schließvorgangs sinnvoll, um zu vermeiden, dass er im Status CLOSE_WAIT oder im Status TIME_WAIT steckt .
  • Wenn Sie Ihre Serveranwendung neu starten müssen, die derzeit über Tausende von Clientverbindungen verfügt, sollten Sie diese Socket-Option festlegen, um Tausende von Server-Sockets in TIME_WAIT zu vermeiden (wenn Sie close() vom Server aus aufrufen). Dies kann dazu führen, dass der Server nach einem Neustart keine Ports für neue Clientverbindungen erhält.
  • Auf Seite 202 in dem oben genannten Buch heißt es speziell: "Es gibt bestimmte Umstände, die die Verwendung dieser Funktion zum Senden eines Abbruchabschlusses rechtfertigen. Ein Beispiel ist ein RS-232-Terminalserver, der möglicherweise für immer in dem Versuch hängen bleibt, CLOSE_WAIT um Daten an einen stecken gebliebenen Terminal-Port zu liefern, würde den stecken gebliebenen Port jedoch ordnungsgemäß zurücksetzen, wenn er ein RST zum Löschen der anstehenden Daten erhalten würde. "

Ich würde empfehlen this langen Artikel, der meiner Meinung nach eine sehr gute Antwort auf Ihre Frage gibt.

168
mgd

Wenn die Wartezeit aktiviert ist, das Timeout jedoch Null beträgt, wartet der TCP Stack nicht auf das Senden ausstehender Daten, bevor die Verbindung geschlossen wird. Daten können dadurch verloren gehen, aber wenn Sie die Wartezeit auf diese Weise einstellen Ich akzeptiere dies und fordere Sie auf, die Verbindung sofort zurückzusetzen, anstatt sie ordnungsgemäß zu schließen.

Vielen Dank an EJP für seinen Kommentar, siehe hier für Details.

16
Len Holgate

Ob Sie den Linger in Ihrem Code sicher entfernen können oder nicht, hängt von der Art Ihrer Anwendung ab: Ist es ein „Client“ (Öffnen TCP Verbindungen und aktives Schließen zuerst) oder ist es ein „ Server “(Hören auf ein TCP öffnen und schließen, nachdem die andere Seite das Schließen eingeleitet hat)?

Wenn Ihre Anwendung den Charakter eines „Clients“ hat (zuerst schließen) UND Sie eine große Anzahl von Verbindungen zu verschiedenen Servern initiieren und schließen (z. B. wenn Ihre App eine Überwachungs-App ist, die die Erreichbarkeit einer großen Anzahl von verschiedenen Servern überwacht), wird Ihre App hat das Problem, dass alle Ihre Clientverbindungen im TIME_WAIT-Status hängen bleiben. Dann würde ich empfehlen, das Zeitlimit auf einen kleineren Wert als den Standardwert zu verkürzen, um das System ordnungsgemäß herunterzufahren, aber die Ressourcen für die Clientverbindungen früher freizugeben. Ich würde das Timeout nicht auf 0 setzen, da 0 mit FIN nicht ordnungsgemäß beendet wird, sondern mit RST abgebrochen wird.

Wenn Ihre Anwendung die Ausprägung eines "Clients" hat und eine große Menge kleiner Dateien von demselben Server abrufen muss, sollten Sie keine neue TCP Verbindung pro Datei herstellen und in einer Eine große Anzahl von Client-Verbindungen in TIME_WAIT, aber lassen Sie die Verbindung offen und rufen Sie alle Daten über dieselbe Verbindung ab. Die Option Linger kann und sollte entfernt werden.

Handelt es sich bei Ihrer Anwendung um einen „Server“ (als Reaktion auf das Schließen des Peers an zweiter Stelle schließen), wird beim Schließen () Ihre Verbindung ordnungsgemäß beendet und Ressourcen werden freigegeben, wenn Sie nicht in den Status TIME_WAIT wechseln. Linger sollte nicht verwendet werden. Wenn Ihre Server-App über einen Überwachungsprozess verfügt, der inaktive offene Verbindungen erkennt, die über einen längeren Zeitraum im Leerlauf sind („long“ muss definiert werden), können Sie diese inaktive Verbindung von Ihrer Seite aus beenden - als eine Art Fehlerbehandlung ansehen - und den Vorgang abbrechen. Dies geschieht, indem die Wartezeit auf 0 gesetzt wird. Close () sendet dann eine RST an den Client und teilt ihm mit, dass Sie wütend sind :-)

5
Grandswiss