2010-08-08 3 views
8

Ich habe eine kleine Anwendung, die Dateien über das Netzwerk an einen Agenten auf einem Windows-Betriebssystem sendet.Was kann ich tun, um TCP Zero Window/TCP Window Full auf der Empfängerseite zu vermeiden?

Wenn diese Anwendung unter Windows ausgeführt wird, funktioniert alles einwandfrei, die Kommunikation ist OK 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 erscheinen. Der Agent schließt die Verbindung nach einigen Minuten.

Der Windows-Linux-Code ist fast der gleiche 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?

EDIT: Hinzufügen des Senden Code - es sieht aus, dass es richtig Teil schreibt Griffe:

int totalSent=0; 
while(totalSent != dataLen) 
{ 
    int bytesSent 
     = ::send(_socket,(char *)(data+totalSent), dataLen-totalSent, 0); 

    if (bytesSent ==0) { 
     return totalSent; 
    } 
    else if(bytesSent == SOCKET_ERROR){ 
#ifdef __WIN32 
     int errcode = WSAGetLastError(); 
     if(errcode==WSAEWOULDBLOCK){ 
#else 
      if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) { 
#endif 
      } 
      else{ 
       if(!totalSent) { 
        totalSent = SOCKET_ERROR; 
       } 
       break; 
      } 
     } 
     else{ 
      totalSent+=bytesSent; 
     } 
    } 
} 

Vielen Dank im Voraus.

+0

Weitere Details? Wird die Datei erfolgreich übertragen, nur mit einer langsameren Geschwindigkeit oder schlägt die Übertragung fehl? Wenn es scheitert, wo versagt es? Passiert etwas oder fällt es auf halbem Wege aus? –

+0

@Robert, danke. Die Übertragung schlägt fehl. Wenn ich einen Ordner mit zB 2 GB 3 KB - 50 KB Dateien übertrage, überträgt er manchmal ~ 0,5 GB, manchmal ~ 1,3 GB Daten und schlägt dann fehl. – rkellerm

+0

Welche Fehlermeldungen erhalten Sie und welche Seite beendet die Verbindung? Verwenden Sie blockierende oder nicht blockierende E/A. Haben Sie einen dedizierten Thread, der I/O erledigt? Je mehr Details, desto besser, und wenn Sie Code-Fragmente buchen könnten, wäre das am besten. –

Antwort

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. Die seltsame Sache ist, dass, wenn ich die Fenstergröße gecheckt hatte, hatte es keinen Einfluss.

Vielen Dank.

+0

Das ist wirklich merkwürdig. In der Regel ist die Deaktivierung von Nagle nur für Echtzeit-Apps nützlich, bei denen Sie eine sehr geringe Latenzzeit auf Kosten der Verschwendung von Bandbreite haben möchten. Die Deaktivierung für die Massenübertragung von Dateien scheint nicht intuitiv zu sein. Haben Sie tatsächlich objektiv getestet und gesehen, dass die Deaktivierung von Nagle den Unterschied macht? Vielleicht könnte eine andere Veränderung, die Sie vorgenommen haben, verantwortlich sein? –

+0

@Robert S. Barnes: Das ist wirklich seltsam, stimme ich zu. Aber das ist die einzige Veränderung, die gemacht wurde, und es hat geholfen. Außerdem hat die Empfängerseite Nagle bereits deaktiviert. Ich weiß, dass es sich auf ein grundlegendes Problem beziehen kann, das sich irgendwo versteckt und darauf wartet, dass es zu einem anderen Zeitpunkt heraus springt und beißt. Aber als Workaround ist es gut genug. – rkellerm

0

Das wahrscheinlichste Problem ist, dass Sie einen Fehler in Ihrem Code haben, wo Sie nicht teilweise Lesevorgänge oder partielle Schreibvorgänge korrekt behandeln. Es ist bekannt, dass TCP zwischen Linux und Windows funktioniert.

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 möglicherweise nicht lesen/schreiben, Sie benötigen normalerweise eine Schleife, um den Überblick zu behalten und sicherzustellen, dass die gesamten Daten übertragen wurden.

12

Wenn Sie Ihren Code nicht sehen, muss ich raten.

Der Grund, warum Sie ein Zero-Fenster in TCP erhalten, liegt darin, 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-Stapel dem Absender ein Zero-Fenster ankündigt.

Nun kann dies Probleme sowohl auf der Sende- als auch auf der Empfangsseite 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, irgendwelche ihrer Daten zu verarbeiten und den Anschein zu erwecken, eingesperrt zu sein.

bearbeiten

Von einigen Ihrer Antworten und Code klingt es wie Ihre Anwendung Gewinde Single ist und Sie versuchen, nicht-Blocking aus irgendeinem Grund sendet zu tun. 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 send(2) hängt, sollten Sie idealerweise eine lange Zeitüberschreitung für den Socket unter Verwendung von setsockopt festlegen und einen separaten Thread für das eigentliche Senden verwenden.

Siehe socket(7):

SO_RCVTIMEO und SO_SNDTIMEO die Empfangs Geben oder das Versenden von Timeouts, bis ein Fehler meldet. Der Parameter ist ein Strukturzeitval. Wenn eine Eingangs- oder Ausgangsfunktion für diese Zeitspanne sperrt und Daten gesendet oder empfangen wurden, wird der Rückgabewert diese Funktion die Menge der übertragenen Daten sein; Wenn keine Daten übertragen wurden und das Zeitlimit erreicht wurde, wird -1 zurückgegeben, wobei errno auf EAGAIN oder EWOULDBLOCK gesetzt ist, genauso wie , wenn der Socket als nicht blockierend festgelegt wurde. Wenn das Zeitlimit auf Null (Standardeinstellung) gesetzt ist, wird die Operation niemals ablaufen.

Ihr Hauptthread kann jede Dateideskriptor in ein queue drücken, um einen Schub für Mutex Warteschlange Zugriff mit sagen, beginnt dann 1 - N Threads die tatsächlichen zu tun Senden I unter Verwendung von blockierenden/O mit Timeouts senden.

Ihre Sendefunktion sollte wie folgt aussehen (vorausgesetzt, Sie ein Timeout sind Einstellung):

// blocking send, timeout is handled by caller reading errno on short send 
int doSend(int s, const void *buf, size_t dataLen) {  
    int totalSent=0; 

    while(totalSent != dataLen) 
    { 
     int bytesSent 
      = send(s,((char *)data)+totalSent, dataLen-totalSent, MSG_NOSIGNAL); 

     if(bytesSent < 0 && errno != EINTR) 
      break; 

     totalSent += bytesSent; 
    } 
    return totalSent; 
} 

Die MSG_NOSIGNAL Flagge stellt sicher, dass Ihre Anwendung nicht durch das Schreiben an eine Steckdose getötet wird, der geschlossen worden ist oder zurückgesetzt durch den Peer. Manchmal werden E/A-Vorgänge durch Signale unterbrochen, und die Überprüfung auf EINTR ermöglicht es Ihnen, die send neu zu starten.

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

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

+0

Danke für diesen Beitrag. Es ist sehr informativ, vor allem die 'MSG_NOSIGNAL', die ich glaube, ist mein Problem in einer meiner Anwendungen. – kuchi

Verwandte Themen