Was kann ich tun, um TCP Zero Window / TCP Window Full auf der Empfängerseite zu vermeiden?

8

Ich habe eine kleine Anwendung, die Dateien über das Netzwerk an einen Agenten auf einem Windows-Betriebssystem sendet.

Wenn diese Anwendung unter Windows läuft, funktioniert alles einwandfrei, die Kommunikation ist in Ordnung und die Dateien werden alle erfolgreich kopiert.

Aber, wenn diese Anwendung unter Linux läuft (RedHat 5.3, der Empfänger ist immer noch Windows) - Ich sehe in Wireshark Netzwerk-Trace-Nachrichten von TCP Zero Window und TCP Window Full, um alle 1-2 Sekunden zu erscheinen. Der Agent schließt dann die Verbindung nach einigen Minuten.

Der Windows-Linux-Code ist fast derselbe und ziemlich einfach. Die einzige nicht-triviale Operation ist setsockopt mit SO_SNDBUF und dem Wert von 0xFFFF. Das Entfernen dieses Codes hat nicht geholfen.

Kann mir bitte jemand mit diesem Problem helfen?

BEARBEITEN: Hinzufügen des sendenden Codes - es sieht so aus, als würde es partielle Schreibvorgänge richtig behandeln:

%Vor%

Vielen Dank im Voraus.

    
rkellerm 08.08.2010, 07:53
quelle

4 Antworten

0

Ich habe versucht, Nagles Algorithmus (mit TCP_NODELAY) zu deaktivieren, und irgendwie hat es geholfen. Die Übertragungsrate ist viel höher, die TCP-Fenstergröße ist nicht voll oder zurückgesetzt. Das Seltsame ist, dass wenn ich die Fenstergröße verändert habe, hat es keine Auswirkungen gehabt.

Danke.

    
rkellerm 19.09.2010, 09:17
quelle
12

Wenn Sie Ihren Code nicht sehen, muss ich raten.

Der Grund, warum Sie ein Zero-Fenster in TCP erhalten, ist, dass im Recv-Puffer des Empfängers kein Platz ist.

Es gibt eine Reihe von Möglichkeiten, wie dies auftreten kann. Eine häufige Ursache für dieses Problem ist, wenn Sie über ein LAN oder eine andere relativ schnelle Netzwerkverbindung senden und ein Computer wesentlich schneller ist als der andere Computer. Als ein extremes Beispiel, sagen Sie, dass Sie einen 3Ghz-Computer haben, der so schnell wie möglich über ein Gigabit-Ethernet zu einem anderen Computer sendet, der eine 1-GHz-CPU betreibt. Da der Sender viel schneller senden kann, als der Empfänger lesen kann, füllt sich der Recv-Puffer des Empfängers, was bewirkt, dass der TCP-Stack dem Absender ein Null-Fenster ankündigt.

Nun kann dies Probleme sowohl auf der sendenden als auch auf der empfangenden Seite verursachen, wenn sie nicht beide bereit sind, damit umzugehen. Auf der sendenden Seite kann dies dazu führen, dass der Sendepuffer voll wird, und Aufrufe, um entweder zu blockieren oder fehlzuschlagen, wenn Sie nicht blockierende E / A verwenden. Auf der Empfängerseite könnten Sie so viel Zeit mit I / O verbringen, dass die Anwendung keine Möglichkeit hat, ihre Daten zu verarbeiten und den Eindruck zu erwecken, eingesperrt zu sein.

Bearbeiten

Aus einigen Ihrer Antworten und Ihrem Code klingt es, als ob Ihre App single threaded ist und Sie aus irgendeinem Grund versuchen, nicht blockierende Sends zu senden. Ich nehme an, dass Sie den Socket in einem anderen Teil des Codes auf Nicht-Blockieren setzen.

Generell würde ich sagen, dass dies keine gute Idee ist. Wenn Sie befürchten, dass Ihre App auf einem send(2) hängen bleibt, sollten Sie im Idealfall eine lange Zeitüberschreitung festlegen Socket mit setsockopt und verwenden Sie einen separaten Thread für das eigentliche Senden.

Siehe Socket (7) :

  

SO_RCVTIMEO und SO_SNDTIMEO       Geben Sie die Zeitüberschreitungen für das Empfangen oder Senden an, bis ein Fehler gemeldet wird. Das   Parameter ist ein Strukturzeitval. Wenn ein   Eingangs- oder Ausgangsfunktionsblöcke für   dieser Zeitraum und Daten wurden   gesendet oder empfangen, der Rückgabewert von   diese Funktion wird die Menge von sein   Daten übertragen; wenn keine Daten vorhanden waren   übertragen und das Timeout wurde   erreicht dann -1 wird mit errno zurückgegeben   auf EAGAIN oder EWOULDBLOCK genau so eingestellt   wenn der Socket angegeben wurde   nicht blockierend. Wenn das Zeitlimit auf eingestellt ist   Null (der Standardwert) dann die Operation   wird nie Timeout.

Ihr Haupt-Thread kann jeden Dateideskriptor in ein queue verschieben, indem Sie einen Boost-Mutex für die Warteschlange verwenden Zugriff, dann starten Sie 1 - N Threads, um das tatsächliche Senden mithilfe blockierender E / A mit Sende-Timeouts durchzuführen.

Ihre Sendefunktion sollte ungefähr so ​​aussehen (vorausgesetzt, Sie setzen eine Zeitüberschreitung):

%Vor%

Das Flag MSG_NOSIGNAL stellt sicher, dass Ihre Anwendung nicht durch das Schreiben in einen Socket beendet wird, der vom Peer geschlossen oder zurückgesetzt wurde. Manchmal werden E / A-Operationen durch Signale unterbrochen, und wenn Sie EINTR prüfen, können Sie send neu starten.

Im Allgemeinen sollten Sie doSend in einer Schleife mit Datenfragmenten von TCP_MAXSEG aufrufen Größe.

Auf der Empfangsseite können Sie eine ähnliche blockierende recv-Funktion schreiben, die ein Timeout in einem separaten Thread verwendet.

    
Robert S. Barnes 08.08.2010 09:04
quelle
1

Ein häufiger Fehler beim Entwickeln mit TCP-Sockets ist eine falsche Annahme über das read () / write () -Verhalten.

Wenn Sie eine Lese- / Schreiboperation ausführen, müssen Sie den Rückgabewert überprüfen, sie müssen die angeforderten Bytes nicht gelesen / geschrieben haben, Sie benötigen normalerweise eine Schleife, um den Überblick zu behalten und sicherzustellen, dass die gesamten Daten übertragen wurden.

>     
João Pinto 08.08.2010 08:36
quelle
0

Das wahrscheinlichste Problem ist, dass Sie einen Fehler in Ihrem Code haben, bei dem Sie nicht teilweise oder teilweise korrekt lesen. Es ist bekannt, dass TCP zwischen Linux und Windows funktioniert.

    
janm 08.08.2010 08:08
quelle