6 Stimmen

Ungewöhnliche tcp-Sperre unter Windows

Wir bewegen große Datenmengen in einem LAN, und das muss sehr schnell und zuverlässig geschehen. Derzeit verwenden wir Windows TCP, wie es in C++ implementiert ist. Bei der Verwendung großer (synchroner) Sendungen werden die Daten viel schneller übertragen als bei einer Reihe kleinerer (synchroner) Sendungen, aber es kommt häufig zu einer Blockierung über große Zeitspannen (.15 Sekunden), wodurch die Gesamtübertragungsrate sinkt. Diese Blockierung tritt unter sehr speziellen Umständen auf, weshalb ich glaube, dass sie gänzlich vermeidbar sein sollte. Noch wichtiger ist, dass wir, wenn wir die Ursache nicht wirklich kennen, auch nicht wissen, dass es bei kleineren Sendungen nicht doch irgendwann einmal passieren wird. Kann jemand diese Blockierung erklären?

Deadlock-Beschreibung (OK, zombie-locked, es ist nicht tot, aber für .15 oder so Sekunden stoppt es, dann startet es wieder)

  1. Die Empfangsseite sendet ein ACK.
  2. Die sendende Seite sendet ein Paket, das das Ende einer Nachricht enthält (Push-Flag ist gesetzt)
  3. Der Aufruf von socket.recv braucht etwa 0,15 Sekunden(!), um zurückzukehren
  4. Ungefähr zu dem Zeitpunkt, zu dem der Anruf zurückkommt, wird von der empfangenden Seite ein ACK gesendet
  5. Das nächste Paket des Absenders wird endlich gesendet (warum wartet es? das TCP-Fenster ist groß genug)

Das Seltsame an (3) ist, dass dieser Anruf in der Regel nicht viel Zeit in Anspruch nimmt und genau die gleiche Datenmenge empfängt. Auf einer 2-GHz-Maschine sind das 300 Millionen Befehle, die Zeit kosten. Ich gehe davon aus, dass der Aufruf nicht (Gott bewahre) darauf wartet, dass die empfangenen Daten vor der Rückkehr zurückgegeben werden, also muss die Rückmeldung auf die Rückkehr des Aufrufs warten, oder beide müssen durch etwas anderes verzögert werden.

Das Problem tritt NIE auf, wenn zwischen 1 und 2 ein zweites Datenpaket (Teil der gleichen Nachricht) eintrifft. Dieser Teil klingt ganz klar so, als ob es damit zu tun hätte, dass Windows TCP ein No-Data-ACK erst dann zurücksendet, wenn entweder ein zweites Paket eintrifft oder ein 200 ms-Timer abläuft. Die Verzögerung beträgt jedoch weniger als 200 ms (sie liegt eher bei 150 ms).

Das dritte ungebührliche Zeichen (und meiner Meinung nach der eigentliche Übeltäter) ist (5). Send wird definitiv aufgerufen, lange bevor die 0,15 Sekunden um sind, aber die Daten kommen NIEMALS auf die Leitung, bevor das ack zurückkommt. Das ist für mich der bizarrste Teil dieses Deadlocks. Es handelt sich nicht um eine TCP-Blockade, da das TCP-Fenster sehr groß ist, da wir SO_RCVBUF auf etwa 500*1460 gesetzt haben (was immer noch unter einem Megabyte liegt). Die Daten kommen sehr schnell herein (im Grunde gibt es eine Schleife, die Daten über Senden ausgibt), so dass der Puffer fast sofort gefüllt sein sollte. Msdn erwähnt, dass verschiedene "Heuristiken" verwendet werden, um zu entscheiden, wann eine Sendung die Leitung erreicht, und dass eine bereits ausstehende Sendung und ein voller Puffer dazu führen, dass send blockiert wird, bis die Daten die Leitung erreichen (andernfalls kopiert send anscheinend wirklich nur Daten in den tcp-Sendepuffer und kehrt zurück).

Warum der Absender in dieser 0,15-Sekunden-Pause keine weiteren Daten sendet, ist für mich allerdings höchst merkwürdig. Die obigen Informationen wurden auf der Empfängerseite mit Wireshark aufgezeichnet (außer natürlich die socket.recv-Rückkehrzeiten, die in einer Textdatei protokolliert wurden). Wir haben versucht, den Sendepuffer auf Null zu setzen und Nagel auf der Senderseite auszuschalten (ja, ich weiß, dass es bei Nagel darum geht, keine kleinen Pakete zu senden - aber wir haben versucht, Nagel auszuschalten, für den Fall, dass dies Teil der unbestätigten "Heuristik" ist, die beeinflusst, ob die Nachricht auf die Leitung geschickt wird. Technisch gesehen besteht Microsofts nagel darin, dass ein kleines Paket nicht gesendet wird, wenn der Puffer voll ist und ein ACK aussteht, also schien es eine Möglichkeit zu sein).

3voto

caf Punkte 224189

Die Sendeblockierung bis zum vorherigen ACK empfangen wird, deutet mit ziemlicher Sicherheit darauf hin, dass das TCP-Empfangsfenster voll ist (Sie können dies überprüfen, indem Sie Wireshark verwenden, um den Netzwerkverkehr zu analysieren).

Unabhängig davon, wie groß Ihr TCP-Fenster ist, wenn die empfangende Anwendung die Daten nicht so schnell verarbeitet, wie sie ankommen, wird das TCP-Fenster irgendwann voll sein. Von welcher Geschwindigkeit ist hier die Rede? Was macht die empfangende Seite mit den Daten? (Wenn Sie die empfangenen Daten auf die Festplatte schreiben, ist es gut möglich, dass Ihre Festplatte nicht mit einem Gigabit-Netzwerk mithalten kann).


OK, Sie haben also ein Empfangsfenster von 730.000 Byte und streamen Daten mit 480 Mbit/s. Das bedeutet, dass es nur 12 ms dauert, bis das Fenster vollständig gefüllt ist. Wenn also die Verzögerung von 150 ms auf der Empfangsseite auftritt, füllt sich das Empfangsfenster fast augenblicklich und bringt den Sender zum Stillstand.

Die Hauptursache ist also diese 150 ms Verzögerung bei der Planung des Empfangsprozesses. Es gibt eine ganze Reihe von Dingen, die das verursachen können (es könnte so einfach sein, dass der Kernel schmutzige Seiten auf die Festplatte spülen muss, um mehr freie Seiten für Ihre Anwendung zu schaffen); Sie könnten Versuchen Sie Erhöhung der Priorität Ihrer Prozesse bei der Planung, aber es gibt keine Garantie dafür, dass dies hilft.

0 Stimmen

Wir haben Wireshark verwendet, um die meisten der oben genannten Informationen zu erhalten. Wir haben ein sehr großes Empfangsfenster (die Fensterskalierung wird von Windows automatisch aufgerufen, da unser Empfangspuffer groß ist). Wir brauchen Geschwindigkeiten in der Größenordnung von mindestens 450 Mbs über 1000Mbs-Ethernet. Das sollte machbar sein. Die Empfangsseite verschiebt die Daten im Moment hauptsächlich im Speicher. Die Pausen entsprechen genau der Zeit, die socket.recv für die Rückkehr benötigt, und nicht der Rückkehrzeit einer anderen Funktion. Es passiert nur unter genau den richtigen Umständen.

0 Stimmen

Wie ich bereits sagte, ist die Größe des Empfangsfensters unerheblich. Prüfen Sie den Wert des Feldes "Window" im TCP-Unterabschnitt der Wireshark-Ausgabe für das verzögerte ACK-Paket.

0 Stimmen

Vielen Dank für die aktualisierte Antwort. Es ist sehr nett von Ihnen, dass Sie sich die Zeit nehmen. Ich wünschte, das wäre der Fall, aber keine der beiden Seiten sendet etwas in diesen 0,15 Sekunden. Anfangs hatten wir einen Empfangspuffer, der zu klein war und der "Fenster"-Wert, der in den tcp-Paketen zurückgeschickt wurde, schrumpfte schnell, wenn Daten ankamen, bis nichts mehr gesendet werden konnte. Das Spielen mit SO_RCVBUF (von der Voreinstellung auf .5Meg bis 50Meg oder mehr) beseitigte dies. Typischerweise ist der Wert von "window" in den tcp-Paketen während dieser Pausen genau SO_RCVBUF (tatsächlich bleibt er während dieser Aufzeichnungen typischerweise einfach die ganze Zeit auf diesem Wert).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X