Laden...

Performance: TCP-Daten möglichst verzögerungsfrei am Client lesen

Erstellt von Mammut vor 10 Jahren Letzter Beitrag vor 10 Jahren 5.407 Views
M
Mammut Themenstarter:in
13 Beiträge seit 2013
vor 10 Jahren
Performance: TCP-Daten möglichst verzögerungsfrei am Client lesen

Hi,

Vorhaben
Ich möchte gerne so schnell wie möglich Daten über TCP Empfangen.

Ausgang
Server sendet ein TCP-Paket, dass 0x600 Bytes => 1536 Bytes groß ist. (maximale Nutzgröße eines TCP-Paketes)

Zum Empfangen nutze ich folgenden Code:


TcpClient tcpclnt = new TcpClient();
tcpclnt.Connect(_ip, _port);
Stream _stream = tcpclnt.GetStream();

int _bufSize = 0x600;
byte _buf = new byte[_bufSize];
int _isClosed;

Stopwatch watch = new Stopwatch();

while(true)
{
  watch.Start();

  _isClosed = _stream.Read(_buf, 0, _bufSize);            
  if (_isClosed == 0) break; //Verbindungsabbruch

  watch.Stop();

  Console.WriteLine(watch.ElapsedMilliseconds + " ms");
  watch.Reset();
}

Problem
Die Zeitmessung besagt, dass der Empfang der Pakete zwischen 5ms und 150ms dauert.

Ich möchte nun gerne, dass die Pakete maximal 50ms brauchen, um Empfangen zu werden, weil der Server in einem 77ms Takt die Pakete sendet.

Wie bekomme ich es hin, dass maximal 50ms benötigt werden, um ein Paket zu empfangen?
Wieso kommt es zu so einer starken Schwankung?

Ich hoffe ihr könnt mir weiterhelfen 😃

Gruß
Mammut

F
115 Beiträge seit 2012
vor 10 Jahren

Hi,

TCP ist nicht dafür gedacht eine bestimmte Latenz zu garantieren, sondern um sicherzustellen, dass alle Pakete beim Empfänger ankommen. Alternativ könntest Du UDP nehmen, dass aber keine Zustellung der Pakete prüft.

Gruß
f_igy

M
Mammut Themenstarter:in
13 Beiträge seit 2013
vor 10 Jahren

Danke für deine Antwort 😃

Wie hoch ist die Wahrscheinlichkeit, dass alle Pakete mit UDP bei einer direkten Verbindung mit einem Cross-Over-Kabel korrekt ankommen? Es ist für mich schon sehr wichtig, dass alle Datenpakete korrekt ankommen.

Ich werde gleich den Socket auf UDP umschreiben. Bin auf die Zeitmessung gespannt.

Gruß
Mammut

Edit:
Lese gerade, dass UDP nicht meinen Anforderungen entspricht, da die Pakete auch in einer unterschiedlichen Reihenfolge ankommen können. Die Reihenfolge ist sehr wichtig!

Weiterer Nachtrag:
Ethernet-Paket
MTU: 1500Bytes
Nutzdaten: 1500Bytes - 20Bytes TCP Header - 20 Bytes IP Header = 1460 Bytes

Quelle: http://www.elektronik-kompendium.de/sites/net/0812211.htm

Vielleicht kommt es dadurch zu den unterschiedlichen Messzeiten?!

Gruß
Mammut

16.835 Beiträge seit 2008
vor 10 Jahren

Je mehr Pakete verschickt werden desto höher steigt in der Regel die Loss-Rate.
Ich meine, dass im > 10 GBit Bereich die Rate schon bei 10% lag. Ist aber in den meisten UDP-Fällen (Spiele, Telefonie, Video) noch völlig irrelevant.

Wenn Du 100% brauchst führt kein Weg an TCP vorbei; egal ob Direktverbindung, Intranet oder Internet.
Garantien bzgl. der Geschwindigkeit kann Dir absolut kein Protokoll bereitstellen; vor allem nicht auf Ethernet-Basis.

Unterschiedliche Messzeiten kommen daher, dass es einfach keine Taktung diesbezüglich und auch keine festen Geschwindigkeiten hier gibt. Wenn Du auf einem Windows System arbeitest gibst ohnehin keine Echtzeit-Übertragungszeiten (übrigens Ethernet eben auch nicht), geschweige denn Echtzeit-Taktungen.
Deine 77ms werden also irgendwo bei 150-200ms sein wenn Du Pech hast.
Da musst Du auf andere Dinge wie Profibus umsteigen.

M
Mammut Themenstarter:in
13 Beiträge seit 2013
vor 10 Jahren

Danke für deine Antwort.

Mein Server läuft auf einem MBED (Microcontroller). Schade, dass Ethernet keine Echtzeit unterstützt.

Ich kenne den Profibus noch nicht. Werde mir den gleich mal anschauen. Dank für den Tip 😃

M
Mammut Themenstarter:in
13 Beiträge seit 2013
vor 10 Jahren

Ich habe erfreuliche Nachrichten.

Das Problem war der Rechenfehler in den Nutzdaten. Ich habe die 0x600 durch 0x5B4 ausgetauscht.

Taktung zum Senden liegt jetzt bei 73ms.
Dauer um das Paket zu Empfangen und in eine Datei zu schreiben: 0,2 - 1 ms.

Danke für eure Hilfe!! 👍 🙂

1.029 Beiträge seit 2010
vor 10 Jahren

Spätestens wenn der GarbageCollector anspringt hast du keine 77ms mehr 😉

Nur so als Nachtrag - für den Fall, dass du wirklich ZWINGEND 77 ms brauchst...

LG

M
Mammut Themenstarter:in
13 Beiträge seit 2013
vor 10 Jahren

Danke für den Tipp 🙂

Hauptsache die Anwendung kommt hinter her und es entsteht kein großer Datenstau, dass die Anwendung abstürzt.

Habe gerade getestet, wie sich das Programm verhält, wenn es 2 Sekunden hängt.

Ergebnis: Stabil! Der GC kann ruhig kommen 😁 Ich denke aber nicht, dass er GC sich anschaltet wird, da ich keinen neuen Speicher deklariere bzw. reserviere (wenn ich Daten empfange) und die Priorität vom GC ist so minimal, dass er wohl in seinen Startlöchern wartet.

Wie groß ist eigentlich der Puffer vom TCP? Habe dazu noch keine Informationen gefunden.

Gruß
Mammut

16.835 Beiträge seit 2008
vor 10 Jahren

Nur so als Grundlagen-Info: der GC ist immer da, immer.
Er läuft im eigenen Thread parallel und überprüft, obs was abzuräumen gibt.

W
872 Beiträge seit 2005
vor 10 Jahren

Wenn Du Dich mehr mit TCP beschaeftigen willst, dann solltest Du DirWindows Internals anschauen - vieles hängt auch vom Treiber Deiner Netzwerkkarte ab.
TCP in den neueren Windows Versionen keine feste Puffergroesse. Wichtig ist vor allem, dass Du in separaten Threads liest, selber pufferst und verarbeitest, da der Puffer nicht sehr gross ist und ein voller Puffer zu Resends fuehrt.
Wenn es um Performance geht, dann gibt es eigene Protokolle, die UDP fuer den Transport und die Reihenfolge und auch fehlende Pakte nachschicken.

M
Mammut Themenstarter:in
13 Beiträge seit 2013
vor 10 Jahren

Hi,

es ist ein unerwarteter Fehler aufgetreten. Die Kommmunikation bricht nach einer unbestimmten Zeit immer wieder ab. Mal nach 2 Minuten mal nach 10 Minuten. Die Zeitmessung sagt, dass das Empfangen von einer Nachricht etwa 1200ms braucht. Danach kommt ein weiteres Paket an und dann sendet der Microcontroller nicht mehr weiter.

Ich vermute, das der Puffer voll ist und des dadurch zu diesem Fehler kommt. Gibt es eine Möglichkeit den Puffer der Netzwerkkarte auszulesen? Oder hat doch der GC seine Finger im Spiel? Wenn ja, gibt es eine Möglichkeit den auszustellen?

Gruß
Mammut

W
872 Beiträge seit 2005
vor 10 Jahren

Benutzt Du einen eigenen Thread nur zum Lesen, der die gelesenen Daten in einer BlockingCollection ablegt?
Ansonsten solltest Du Dich mit tcpdump/windump vertraut machen, damit Du Fehler auf TCP-Protokollebene sehen kannst - das Betriebssytem verrät nix darüber.

M
Mammut Themenstarter:in
13 Beiträge seit 2013
vor 10 Jahren

Danke für deine Antwort!!

Ich habe den Netzwerkverkehr mitgeschnitten. Das Ergebnis ist ernüchternd... Meine Dropbox funkt mir dazwischen -.- Wenn ich die Dropbox ausschalte läuft es.

1: Das Paket wird versendet

2: Dann kommt 5x folgendes Paket:
318 17.179918000 192.168.0.101 255.255.255.255 DB-LSP-DISC 175 Dropbox LAN sync Discovery Protocol

3: Dann kommt er der Ack vom versendeten Paket.

Das Ack ist nun so spät dran, dass der Versender denkt, dass das Paket nicht angekommen wäre. Zwischen Senden und Ack liegen ca 60-70ms.

Jetzt fehlt noch ein Fall:
Was passiert, wenn ein Paket wirklich falsch ankommt und erneut geschickt werden muss? Ich konnte meinen Sendespeicher im Microcontroller bis auf 10 Nachrichten buffern. Danach ist der Speicher im Microcontroller voll. Ich hoffe, dass das reicht, sonst muss ich wirklich umsteigen X(

Gruß
Mammut

EDIT:
Ich verwende keine blockierenden Collections. Die Collections sind mir zu langsam. Sie haben leider nur einen Timeout von 1ms. Ich arbeite mit einem Interupt der in 0,1ms getaktet ist. Falls nun ein Fehler in der Collection auftritt und der Timeout abläuft, wurde die Collection schon wieder 10x aufgerufen und dann sitze ich im "Deadlock".

Ich möchte nicht, dass das Programm dann blockiert, da man dann keine gescheite Fehlermeldung erhält und darauf nicht reagieren kann.

Das einzige was blockierend bzw. wartend ist, ist das senden.

16.835 Beiträge seit 2008
vor 10 Jahren

Dir ist schon klar, dass weder Windows noch .NET Echtzeitgfähig ist und Du niemals an Deine 0,1ms ran kommen wirst?
Thread-sicherheit ist ein absolutes Muss. Du musst Deine Collections locken oder Thread-fähige Collections nutzen, ansonsten hauen Dir die Collections früher oder später reihenweise Exceptions um die Ohren.

M
Mammut Themenstarter:in
13 Beiträge seit 2013
vor 10 Jahren

Ja ist mir klar. Die 0.1ms laufen auch auf dem Microcontroller. Der Speicher den ich fülle ist auch Thread und Interupt sicher. Falls es zum Überschreiben von Daten kommt, die noch nicht versendet wurden, bekomme ich eine Meldung und der Microcontroller hört auf zu senden.

Der Computer (Windows und .Net) bekommt alle 70ms ein Paket, dass er entgegennimmt und speichert. Dafür braucht er ca. 2ms. Wenn der PC für mehr als 3 Sekunden hängt, dann knallt es. Das ist mir auch bewusst. Ich habe leider keine anderen Mittel zur Verfügung.

Letzte Lösung, wenn diese nicht stabil läuft, wäre eine SD-Karte im Microcontroller einzubauen und darauf die Daten zu speichern. Und dann die Daten von der SD-Karte nach und nach an den Computer zu versenden. Dann kann ich auch mit den Collections arbeiten. Auf die SD-Karte mag ich aber noch gerne verzichten.

Gruß
Mammut

W
872 Beiträge seit 2005
vor 10 Jahren

Ich denke Du hast auch ein falsches Verständnis, was ein Timeout ist.
Ein Timeout ist, wenn nix kommt, nicht wie lange hinzufügen/entfernen dauert.
Ich habe gestern in einem meiner Programme auf einem echten Server (Xeon Sandybridge) über mehrere Stunden im Durchschnitt ca. 2500 Elemente/Sekunde im Schnitt in eine BlockingCollection hinzugefügt und entfernt. Nichtsdestotrotz kann es durch den GC Spitzen geben, wo Du mehrere 100 ms für einmal lesen/schreiben brauchst - bei mir lag die Spitze gestern bei 750 ms.

R
74 Beiträge seit 2006
vor 10 Jahren

Ein Denkanstoß ...

ich weiss nicht wie BlockingCollection intern arbeitet, aber
diese muss evtl. nicht zwingend verwendet werden um threadsafe zu arbeiten.

Win32-API: InterlockedIncrement

Für C# sollte diese Funktion auch in einem entsprechendem Namespace
vorhanden sein.

Kombiniert mit einem Ringbuffer sollte sich sowas wie hier implementieren lassen

http://www.codeproject.com/Articles/43510/Lock-Free-Single-Producer-Single-Consumer-Circular

Wobei ich glaube, die Daten auf SD Karte zusätzlich zu puffern ist die sinnvollste, wenn diese wichtig sind !

Wenn diese unwichtig sind, kann es auch egal sein ob bei der Übertragung
Fehler auftreten oder nicht 😃

16.835 Beiträge seit 2008
vor 10 Jahren

BlockingCollection ist für Queueing gedacht; normalerweise im Zusammenhang mit vielen Consumer (Pipelining).
Ich habe festgestellt, dass List mit lock() performanter ist, als der ConcurrentBag; obwohl intern auch eine List verwendet wird.

Es führt hier aber KEIN Weg vorbei, die Liste zu synchronisieren; egal welche.
Auch wenn nur Queue<T> verwendet wird, muss diese synchronisiert werden.