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.
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.
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.
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.
>Tags und Links c++ cross-platform network-programming tcp