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)
- Die Empfangsseite sendet ein ACK.
- Die sendende Seite sendet ein Paket, das das Ende einer Nachricht enthält (Push-Flag ist gesetzt)
- Der Aufruf von socket.recv braucht etwa 0,15 Sekunden(!), um zurückzukehren
- Ungefähr zu dem Zeitpunkt, zu dem der Anruf zurückkommt, wird von der empfangenden Seite ein ACK gesendet
- 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).