2009-09-23 5 views
13

Ich mache meine eigene benutzerdefinierte Server-Software für ein Spiel in Java (das Spiel und Original-Server-Software wurden mit Java geschrieben). Es gibt keine Protokolldokumentation, daher muss ich die Pakete mit Wireshark lesen.Was ist ein TCP-Fensterupdate?

Während ein Client den Server verbindet, sendet er die Level-Datei im Gzip-Format. Bei ungefähr 94 Paketen, die den Level senden, stürzt mein Server den Client mit einer ArrayIndexOutOfBoundsException ab. Laut der Capture-Datei vom ursprünglichen Server wird an diesem Punkt ein TCP-Window-Update gesendet. Was ist ein TCP Window Update und wie würde ich einen mit einem SocketChannel senden?

Antwort

50

TCP-Fenster werden für die Flusssteuerung zwischen den Peers einer Verbindung verwendet. Mit jedem ACK-Paket sendet ein Host ein "Fenstergröße" -Feld. In diesem Feld wird angegeben, wie viele Datenbytes der Host empfangen kann, bevor er voll ist. Der Sender soll nicht mehr als diese Datenmenge senden.

Das Fenster wird möglicherweise voll, wenn der Client die Daten nicht schnell genug empfängt. Mit anderen Worten, die TCP-Puffer können sich füllen, während die Anwendung ausgeschaltet ist und etwas anderes als das Lesen von ihrem Socket ausführt. Wenn dies geschieht, würde der Client ein ACK-Paket mit dem gesetzten "Fenster voll" -Bit senden. An diesem Punkt soll der Server aufhören, Daten zu senden. Alle Pakete, die an ein Gerät mit einem vollen Fenster gesendet werden, werden nicht bestätigt. (Dies führt dazu, dass sich ein Sender mit schlechtem Verhalten erneut sendet. Ein Sender mit gutem Verhalten puffert die ausgehenden Daten. Wenn der Puffer auf der sendenden Seite ebenfalls voll ist, wird die sendende Anwendung blockiert, wenn sie versucht, mehr Daten in den Socket zu schreiben !)

Dies ist ein TCP-Stall. Es kann aus vielen Gründen passieren, aber letztlich bedeutet es nur, dass der Sender schneller sendet, als der Empfänger liest.

Sobald die App auf der Empfangsseite wieder zum Lesen aus dem Socket kommt, werden einige der gepufferten Daten gelöscht, was etwas Speicherplatz freigibt.Der Empfänger sendet dann ein "Fensteraktualisierungs" -Paket, um dem Absender mitzuteilen, wie viele Daten er übertragen kann. Der Sender beginnt mit der Übertragung seiner gepufferten Daten und der Datenverkehr sollte normal verlaufen.

Natürlich können Sie wiederholte Ställe erhalten, wenn der Empfänger durchgängig langsam ist.

Ich habe dies so formuliert, als ob Sender und Empfänger unterschiedlich sind, aber in Wirklichkeit tauschen beide Peers Fensteraktualisierungen mit jedem ACK-Paket aus, und beide Seiten können ihr Fenster ausfüllen.

Die allgemeine Nachricht ist, dass Sie Fensteraktualisierungspakete nicht direkt senden müssen. Es wäre eigentlich eine schlechte Idee, einen aufzuspielen.

In Bezug auf die Ausnahme, die Sie sehen ... wird es wahrscheinlich nicht verursacht oder durch das Fenster-Update-Paket verhindert werden. Wenn der Client jedoch nicht schnell genug liest, verlieren Sie möglicherweise Daten. Auf Ihrem Server sollten Sie den Rückgabewert Ihrer Socket.write() -Aufrufe überprüfen. Es könnte weniger als die Anzahl der Bytes sein, die Sie schreiben möchten. Dies passiert, wenn der Sendepuffer des Senders voll wird, was während eines TCP-Stalls passieren kann. Sie könnten Bytes verlieren.

Zum Beispiel, wenn Sie versuchen, 8192 Bytes bei jedem Aufruf schreiben zu schreiben, aber eine der Anrufe zurück 5691, dann müssen Sie die verbleibenden 2501 Bytes auf den nächsten Anruf senden. Andernfalls wird der Client den Rest dieses 8K-Blocks nicht sehen und Ihre Datei wird auf der Clientseite kürzer sein als auf der Serverseite.

+3

Mein Bug-Fu sagt mir, dass dieser letzte Absatz sehr wahrscheinlich das Problem genagelt haben wird. – caf

+1

Große Erklärung, danke! Genau das, was ich brauchte. Ich glaube, ich fand die Ursache der Ausnahme - der Client wurde erwartet, dass die Daten in einer anderen, etwas längeren Form als das, was ich sendete. – phpscriptcoder

+0

+1 für die detaillierte Erklärung! – Izza

7

Dies passiert wirklich tief im TCP/IP-Stack; In Ihrer Anwendung (Server und Client) müssen Sie sich nicht um TCP-Fenster kümmern. Der Fehler muss etwas anderes sein.

2

Ein TCP Window Update hat mit der Kommunikation der verfügbaren Puffergröße zwischen dem Sender und dem Empfänger zu tun. Eine ArrayIndexOutOfBoundsException ist nicht die wahrscheinliche Ursache dafür. Am wahrscheinlichsten ist, dass der Code eine Art von Daten erwartet, die er nicht bekommt (möglicherweise schon lange vorher). Ohne den Code und den Stack-Trace zu sehen, ist es wirklich schwer, mehr zu sagen.

+0

Danke für die Hilfe. Der Stack-Trace ist nicht allzu hilfreich, da der Client-Code verschleiert ist, so dass alle Klassen und Methoden einzelne Buchstaben sind ... Mehr Forschung ist notwendig! – phpscriptcoder

+0

@phpscriptcoder: Vielleicht sollten Sie den ursprünglichen Programmierer (s) gefeuert bekommen ... –

0

Erhalten Sie eine details mit der exception?

Es ist nicht wahrscheinlich das
Paket aktualisiert TCP-Fenster verwendet (haben Sie es für mehrere Instanzen wiederholen genau gesehen?)

Wahrscheinlicher auf Ihren Verarbeitungscode im Zusammenhang, die auf den empfangenen Daten arbeiten.

0

Dies ist normalerweise nur ein Auslöser, nicht die Ursache für Ihr Problem.

Wenn Sie beispielsweise den NIO-Selektor verwenden, kann ein Fensterupdate das Aufwachen eines schreibenden Kanals auslösen. Dies wiederum löst die fehlerhafte Logik in Ihrem Code aus.

Holen Sie sich einen Stacktrace und es zeigt Ihnen die Ursache.

2

TCP WindowUpdate - Dies zeigt an, dass das Segment ein reines WindowUpdate-Segment war. Ein WindowUpdate tritt auf, wenn die Anwendung auf der Empfangsseite bereits empfangene Daten aus dem RX-Puffer verbraucht hat, was bewirkt, dass die TCP-Schicht ein WindowUpdate an die andere Seite sendet, um anzuzeigen, dass nun mehr Platz im Puffer verfügbar ist. Wird normalerweise angezeigt, nachdem eine TCP ZeroWindow-Bedingung aufgetreten ist. Sobald die Anwendung auf dem Empfänger Daten aus dem TCP-Puffer abruft und dadurch Speicherplatz freigibt, sollte der Empfänger den Sender darüber informieren, dass die TCP-ZeroWindow-Bedingung nicht mehr existiert, indem er ein TCP-WindowUpdate sendet, das die aktuelle Fenstergröße ankündigt.

https://wiki.wireshark.org/TCP_Analyze_Sequence_Numbers