Ich glaube, ich verstehe die formale Bedeutung der Option. In einigen Legacy-Codes, die ich gerade verarbeite, wird die Option verwendet. Der Kunde beschwert sich über RST als Antwort 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?
Antworten:
Der typische Grund für das
SO_LINGER
Festlegen eines Zeitlimits von Null besteht darin, eine große Anzahl von Verbindungen imTIME_WAIT
Status zu vermeiden und alle verfügbaren Ressourcen auf einem Server zu binden.Wenn eine TCP-Verbindung sauber geschlossen wird, bleibt die Verbindung am Ende, das das Schließen initiiert hat ("aktives Schließen"),
TIME_WAIT
einige Minuten lang bestehen. Wenn es sich bei Ihrem Protokoll also um ein Protokoll handelt, bei dem der Server das Schließen der Verbindung initiiert und eine sehr große Anzahl kurzlebiger Verbindungen umfasst, ist dieses Problem möglicherweise anfällig.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 besser, das Protokoll so zu gestalten, dass der Client den Verbindungsabschluss nach Möglichkeit initiiert.quelle
TIME_WAIT
sitzt der Wille beim Kunden und schadet ihm nicht. Denken Sie daran, wie es in der dritten Ausgabe von "UNIX Network Programming" (Stevens et al.) Auf Seite 203 heißt: "Der Status TIME_WAIT ist Ihr Freund und hilft uns. Anstatt zu versuchen, den Status zu vermeiden, sollten wir ihn verstehen (Abschnitt 2.7). . "Lesen Sie für meinen Vorschlag den letzten Abschnitt: „Wann wird SO_LINGER mit Timeout 0 verwendet?“ .
Bevor wir dazu kommen, ein kleiner Vortrag über:
TIME_WAIT
FIN
,ACK
undRST
Normale TCP-Beendigung
Die normale TCP-Beendigungssequenz sieht folgendermaßen aus (vereinfacht):
Wir haben zwei Kollegen: A und B.
close()
FIN
an B.FIN_WAIT_1
ZustandFIN
ACK
an A.CLOSE_WAIT
ZustandACK
FIN_WAIT_2
Zustandclose()
FIN
an A.LAST_ACK
ZustandFIN
ACK
an B.TIME_WAIT
ZustandACK
CLOSED
Zustand - dh wird aus den Socket-Tabellen entferntZEIT WARTET
Der Peer, der die Kündigung initiiert - dh
close()
zuerst anruft -, landet also imTIME_WAIT
Status.Um zu verstehen, warum der
TIME_WAIT
Staat 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
TIME_WAIT
auf einem Server sein, da möglicherweise verhindert wird, dass neue Verbindungen akzeptiert werden.Um dieses Problem zu umgehen, habe ich viele Vorschläge gesehen, die Socket-Option SO_LINGER mit dem Timeout 0 vor dem Aufruf festzulegen
close()
. Dies ist jedoch eine schlechte Lösung, da dadurch die TCP-Verbindung mit einem Fehler beendet wird.Entwerfen Sie stattdessen Ihr Anwendungsprotokoll so, dass die Verbindungsbeendigung immer von der Clientseite aus initiiert wird. Wenn der Client immer weiß, wann er alle verbleibenden Daten gelesen hat, kann er die Beendigungssequenz initiieren. Ein Browser weiß beispielsweise aus dem
Content-Length
HTTP-Header, wann er alle Daten gelesen hat, und kann das Schließen einleiten. (Ich weiß, dass es in HTTP 1.1 für eine mögliche Wiederverwendung eine Weile geöffnet bleibt und dann geschlossen wird.)Wenn der Server die Verbindung schließen muss, entwerfen Sie das Anwendungsprotokoll so, dass der Server den Client zum Aufrufen auffordert
close()
.Wann wird SO_LINGER mit Timeout 0 verwendet?
Gemäß der dritten Ausgabe von "UNIX Network Programming", Seite 202-203, führt die Einstellung
SO_LINGER
mit Timeout 0 vor dem Aufrufclose()
dazu, dass die normale Beendigungssequenz nicht initiiert wird.Stattdessen
close()
sendet der Peer, der diese Option einstellt und aufruft , einRST
(Verbindungs-Reset), das auf einen Fehlerzustand hinweist, und so wird es am anderen Ende wahrgenommen. In der Regel werden Fehler wie "Verbindung durch Peer zurückgesetzt" angezeigt.Daher ist es in der normalen Situation eine wirklich schlechte Idee,
SO_LINGER
vor dem Anruf das Zeitlimit 0 festzulegenclose()
- von nun an angerufen eine Serveranwendung abortives Schließen bezeichnet .Bestimmte Situationen rechtfertigen dies jedoch trotzdem:
CLOSE_WAIT
oder in dieser endetTIME_WAIT
Status Status .TIME_WAIT
(wenn Sieclose()
vom Server aus anrufen ), da dies möglicherweise verhindert, dass der Server verfügbare Ports für neue Clientverbindungen erhält nach dem Neustart.CLOSE_WAIT
Versuch, Daten an ein feststeckendes Terminal zu liefern , möglicherweise für immer hängen bleibt Port, würde aber den stecken gebliebenen Port ordnungsgemäß zurücksetzen, wenn erRST
die ausstehenden Daten verwerfen müsste . "Ich würde diesen langen Artikel empfehlen, der meiner Meinung nach eine sehr gute Antwort auf Ihre Frage gibt.
quelle
TIME_WAIT
ist nur dann ein Freund, wenn es keine Probleme verursacht: stackoverflow.com/questions/1803566/…Wenn die Verweilzeit aktiviert ist, das Zeitlimit jedoch Null ist, wartet der TCP-Stapel nicht auf das Senden ausstehender Daten, bevor die Verbindung geschlossen wird. Aufgrund dessen können Daten verloren gehen. Wenn Sie jedoch das Verweilen auf diese Weise einstellen, akzeptieren Sie dies und fordern, dass die Verbindung sofort zurückgesetzt wird, anstatt ordnungsgemäß geschlossen zu werden. Dies führt dazu, dass eine RST anstelle der üblichen FIN gesendet wird.
Vielen Dank an EJP für seinen Kommentar, siehe hier für Details.
quelle
Ob Sie die Verweildauer in Ihrem Code sicher entfernen können oder nicht, hängt von der Art Ihrer Anwendung ab: Ist es ein „Client“ (TCP-Verbindungen öffnen und zuerst aktiv schließen) oder ist es ein „Server“ (Abhören eines offenen TCP und Schließen, nachdem die andere Seite das Schließen eingeleitet hat)?
Wenn Ihre Anwendung den Geschmack 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 verschiedener Server überwacht), Ihre App hat das Problem, dass alle Ihre Client-Verbindungen im Status TIME_WAIT stecken 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 Clientverbindungen früher freizugeben. Ich würde das Timeout nicht auf 0 setzen, da 0 mit FIN nicht ordnungsgemäß heruntergefahren, sondern mit RST abgebrochen wird.
Wenn Ihre Anwendung das Flair eines „Clients“ hat und eine große Anzahl kleiner Dateien vom selben Server abrufen muss, sollten Sie keine neue TCP-Verbindung pro Datei initiieren und in TIME_WAIT eine große Anzahl von Client-Verbindungen erhalten, sondern Lassen Sie die Verbindung offen und rufen Sie alle Daten über dieselbe Verbindung ab. Die verweilende Option kann und sollte entfernt werden.
Wenn Ihre Anwendung ein „Server“ ist (schließen als Reaktion auf das Schließen des Peers an zweiter Stelle), wird Ihre Verbindung bei close () ordnungsgemäß beendet und Ressourcen werden freigegeben, wenn Sie nicht in den Status TIME_WAIT wechseln. Verweilzeit sollte nicht verwendet werden. Wenn Ihre Server-App jedoch über einen Überwachungsprozess verfügt, der erkennt, dass inaktive offene Verbindungen für längere Zeit im Leerlauf sind („lang“ ist zu definieren), können Sie diese inaktive Verbindung von Ihrer Seite aus herunterfahren - sehen Sie dies als eine Art Fehlerbehandlung an - mit einem fehlgeschlagenen Herunterfahren. Dies erfolgt durch Setzen des Zeitlimits für das Verweilen auf 0. close () sendet dann eine RST an den Client und teilt ihm mit, dass Sie wütend sind :-)
quelle
Auf Servern möchten Sie möglicherweise senden,
RST
anstattFIN
Clients mit schlechtem Verhalten zu trennen. Dies überspringt,FIN-WAIT
gefolgt vonTIME-WAIT
Socket-Status auf dem Server, wodurch verhindert wird, dass Serverressourcen erschöpft werden, und schützt somit vor dieser Art von Denial-of-Service-Angriff.quelle
Ich mag Maxim's Beobachtung, dass DOS-Angriffe Serverressourcen erschöpfen können. Es passiert auch ohne einen tatsächlich böswilligen Gegner.
Einige Server müssen sich mit dem "unbeabsichtigten DOS-Angriff" befassen, der auftritt, wenn die Client-App einen Fehler mit einem Verbindungsleck aufweist, bei dem sie für jeden neuen Befehl, den sie an Ihren Server senden, eine neue Verbindung herstellen. Und dann vielleicht ihre Verbindungen schließen, wenn sie auf GC-Druck stoßen, oder vielleicht die Verbindungen irgendwann auslaufen.
Ein anderes Szenario ist das Szenario "Alle Clients haben dieselbe TCP-Adresse". Dann sind Clientverbindungen nur durch Portnummern unterscheidbar (wenn sie eine Verbindung zu einem einzelnen Server herstellen). Und wenn Clients aus irgendeinem Grund schnell mit dem Öffnen / Schließen von Verbindungen beginnen, können sie den Tupelbereich (Client-Adresse + Port, Server-IP + Port) erschöpfen.
Daher denke ich, dass Server am besten empfohlen werden, zur Linger-Zero-Strategie zu wechseln, wenn sie eine hohe Anzahl von Sockets im Status TIME_WAIT sehen - obwohl dies das Clientverhalten nicht behebt, kann es die Auswirkungen verringern.
quelle
Der Listen-Socket auf einem Server kann mit der Zeit 0 verweilen, um sofort auf die Bindung an den Socket zuzugreifen und alle Clients zurückzusetzen, deren Verbindungen noch nicht hergestellt sind. TIME_WAIT ist etwas, das nur interessant ist, wenn Sie ein Netzwerk mit mehreren Pfaden haben und möglicherweise falsch geordnete Pakete erhalten oder sich auf andere Weise mit ungerader Netzwerkpaketbestellung / Ankunftszeit befassen.
quelle