![]() |
Indy-Test optimieren
Liste der Anhänge anzeigen (Anzahl: 3)
Hallo zusammen,
nachfolgendes ist nur etwas für Leute mit starken Nerven - bzw. mit Kenntnissen von Indy und ausreichend Tagesfreizeit ;-) Also: Ich habe ein kleines Testprojekt (XE3)... Server: Ein Server verwaltet ein Integer-Array und stellt die Werte als Linien in einem Bitmap dar, das über einen Timer in das Formular kopiert wird. Die Werte werden durch das Array geschoben, um einen optischen Effekt zu haben. Klick in das Formular dreht die Darstellung jeweils um 90 Grad. Das Array und der Turn(Dreh)-Wert sind einfach mal global verwaltet und reine Integer, damit es keine Syncro-Probleme gibt. Clients: Die Clients haben die gleiche Datenstruktur und Darstellung. Aber über Indy werden nacheinander zyklisch alle Zahlenwerte und der Turn-Wert abgerufen. Dieser Abruf läuft in einem Thread, damit das Formular "bedienbar" bleibt. Ein Formular-Click sendet in dem Fall einen Dreh-Auftrag an den Server. Die Werte-Übertragung läuft über Streams (mit Umweg über eine StringList), damit optional auch Umlaute übertragen werden könnten. Im Grunde funktioniert das soweit - ABER! Das ist natürlich recht langsam und die Clients bleiben gelegentlich auch hängen. Ich weiß, dass ich in dem Testprojekt VIELES falsch mache. Aber zumindest das globale Array bitte ich hier mal großzügig zu übersehen. Mir geht es hier darum, den Datenaustausch zwischen Server und Client zu optimieren und stabil zu gestalten. Real soll der Server eine Menge von Daten verwalten, von denen sich die Clients nach Bedarf einige abrufen bzw. auch Aufträge den Server senden. Über einen abzurufenden Zeitstempel können die Clients prüfen, ob der Server über neue Daten verfügt. Wenn nicht, werden gepufferte Daten genutzt. Hier im Beispiel will ich diese Prüfungen vernachlässigen. Wenn es gelingt, die Grafik in den Clients flüssig aktualisieren zu lassen wäre ich schon sehr zufrieden. Bei Indy würde ich eigentlich gern bleiben, da man so ja auch noch andere Möglichkeiten wie eMail usw. hätte. Es wäre aber auch ein genereller Wechsel möglich, wenn das etwas bringen würde. Drei Hinweise noch: - natürlich will ich nicht das Bitmap vom Server an die Clients schicken - und die Clients können nicht wissen, welche Werte sich auf dem Server geändert haben. - Die realen Daten können auch Sonderzeichen und Umlaute enthalten Wäre super, wenn sich das jemand mal anschauen könnte... Anbei - Video - XE3-Projekt - Exen (in den Ini´s müsstet Ihr am besten Eure IP eintragen) (Sorry, ist halt ziemliches Stückelwerk.) |
AW: Indy-Test optimieren
Was hast du denn für ein Problem mit den Strings und Umlauten und Streams? :gruebel:
Dafür gibt es Hilfsklassen (z.B. ![]() ![]() |
AW: Indy-Test optimieren
(ich muss das jetzt mal aus der Erinnerung zusammen fassen)
Socket.Writeln(' Text mit é ') führte immer zu einer Verstümmelung. Je nachdem welche Textart ich angegeben habe (UTF8 o.ä.) wurden auch Umlaute verstümmelt. Eine fehlerfreie Textübertragung mit Sonderzeichen und Umlauten habe ich nicht hin bekommen. Daher der Weg über Streams. TStreamReader und TStreamWriter waren mir nicht mehr geläufig. Andererseits hatte ich auch überlegt, die Anweisung und Parameter ohnehin als StringList zu übertragen: Zitat:
Meine wichtigere Frage wäre aber die Gestaltung des Datenaustausches gewesen. Das ist sicher noch sehr suboptimal, insbesondere wenn mehrere Clients Daten abrufen. Wie machen das denn Onlinespiele? Im Grunde könnte man ja die laufenden Linien als Spielfiguren ansehen und die Clients rufen ab, welche Linie (oder Figur) jetzt wo und wie gezeichnet werden muss. Meine Lösung ist einfach viel zu langsam und ich weiß nicht, wie man das besser gestalten könnte. |
AW: Indy-Test optimieren
Was ist denn nun genau langsam? Der Server, die Clients oder was? Ich habe mir jetzt nur den Server angeschaut. Da kann man schon Einiges optimieren. Aber das wäre ehe "Feintuning". Versuch doch mal herauszufinden, in welchen Routinen / Zeilen am meisten Zeit verbaten wird.
Als erstes würde ich aber auf das Ganze auf UDP umstellen, da Du sonst den ganzen Protokoll-Ballast mitschleppst. Es sein denn Du willst mal irgendwas tunneln oder es womöglich als http tarnen. |
AW: Indy-Test optimieren
Bei einem Online-Spiel können die Daten extrem reduziert werden.
Ein Objekt (Spieler, Rakete, Baum) ist vom Aussehen und Verhalten klar definiert und wird wenn nötig am Anfang des Spiels einmal übertragen (Farben, Polygone, Texturen, ...) Bei Änderungen werden nur noch Position und Status übertragen und nicht das gesamte Objekt. z.B. Rakete fliegt mit folgendem Bewegungsvektor [x,y,z] von Position [x,y,z] und nur weil die Rakete weiterfliegt, werden keine neuen Daten übertragen, denn das Flugverhalten ist bekannt und kann ohne weitere Daten von jedem Client berechnet werden. |
AW: Indy-Test optimieren
@Sir Rufo
Ja aber ich habe im Beispiel auch nur 300 Zahlenwerte. @Union Was genau so langsam ist habe ich noch nicht gecheckt. Ist im Zusammenspiel zwischen Server und Clients wohl auch nicht ganz so einfach. Vielleicht ist das zeitliche Verhalten ja auch in Ordnung und gar nicht mehr gravierend zu verbessern. Dann wäre mein ursprüngliches Ziel auf dem Wege wohl nicht zu erreichen. Es hätte natürlich auch sein können, dass mein Server oder die Clients grundsätzlich falsch aufgebaut sind. UDP ist ja offenbar eine fehleranfälligere Übertragungsform. Fehler müssten dann also explizit geprüft und ausgebessert werden. Würde das deutliche Geschwindigkeitsvorteile bringen? Eigentlich muss die Datenübertragung zuverlässig im lokalen Netz und evtl. auch über Internet funktionieren. Genauere Anforderungen habe ich derzeit nicht. |
AW: Indy-Test optimieren
Warum versuchen denn sämtliche Programme, die schnell und viele Daten übertragen müssen, unbedingt alles auf UDP umbiegen (z.b. Skype)? Das bringt gewaltige Vorteile im Bereich der Übertragung. Im lokalen Netzwerk macht es sich nicht ganz so gravierend bemerkbar.
Noch mal mein Vorchlag: Die Teile sind ja momentan noch winzig. Also hast Du gute Chancen die Bremsen zu finden durch exakte Messung. Schau Dir mal System.Diagnostics an. |
AW: Indy-Test optimieren
Zitat:
|
AW: Indy-Test optimieren
Man braucht die Pakete für die Clients nur durchnumerieren. Die dann zu implementierende Prüfungslogik ist trival. Aber stahli sollte das trotzdem zunächst mal profilen - unabhängig vom Übertragungsprotokoll.
|
AW: Indy-Test optimieren
Nun, die erste Optimierungsmöglichkeit die auffällt ist:
anstatt für jeden einzelnen Integerwert eine Verbindung zu Server aufzubauen, einen Request zu senden, den Integerwert abzuholen (mit S := ReadText(IdTCPClient1, '?' + IntToStr(Index));) und dann die TCP Verbindung zu schliessen, nur einmal pro Zyklus die Verbindung aufbauen und einen kompletten Datenblock (300 Integerwerte) abrufen. Und die Verbindung nur nach einem Datenblock schliessen, oder nur bei Fehlern und beim Programmende. Ein potentieller Störfaktor ist das "fForm.Caption := DateTimeToStr(Now);", das ist ein nicht-threadsicherer Zugriff auf ein Objekt der Oberfläche. |
AW: Indy-Test optimieren
Zitat:
TCP-Verbindungsaufbau: 3 Pakete TCP-Verbindungsaufbau: 2 Pakete Das sind dann 300 * (5 + 1) = 1800 Pakete Schickt man alles auf einen Rutsch sind das: 5 Pakete + ca. 5 Pakete für Nutzdaten = 10 Pakete 1800 / 10 = 180 Die ACK-Pakete habe ich nicht berücksichtigt, aber das dürfte wenig ändern |
AW: Indy-Test optimieren
Oh Mann, da hätte "man" ja auch mal selbst drauf kommen können. :oops:
Es ist ja nachvollziehbar, dass der Verbindungsaufbau immer etwas Zeit kostet. Jetzt übertrage ich die 301 Werte einfach als String-Liste und lese sie en Block wieder aus. Das funktioniert hier auf dem PC erst mal ohne sichtbare Verzögerung, auch bei mehreren Clients.
Delphi-Quellcode:
Wenn ich später von 1Mio Datensätzen 300 abrufen will, dann werde ich einfach die 300 Id`s, RecNo o.ä. in einer Anfrageliste übergeben und die Ergebnisse dann vom Server aus anfügen und zurück schicken.
// Server:
.. else if (cmd = '?ALL') then begin S := IntToStr(T); for Index := Low(A) to High(A) do S := S + #13#10 + IntToStr(A[Index]); SendText(AContext, S); end .. // Client: .. procedure ReadText(FClient: TIdTCPClient; cmd: String; SL: TStringList); var MS: TMemoryStream; I: Int64; begin FClient.Connect; if FClient.Connected then begin MS := TMemoryStream.Create; try FClient.Socket.WriteLn(cmd); I := FClient.Socket.ReadInt64; FClient.Socket.ReadStream(MS, I, false); MS.Position := 0; SL.LoadFromStream(MS); // Result := Copy(SL.Text, 1, Length(SL.Text) - 2); // SL.Text; // finally MS.Free; end; end; FClient.Disconnect; end; .. ReadText(IdTCPClient1, '?ALL', SL); T := StrToInt(SL[0]); Index := 1; while Index < SL.Count do begin A[Index] := StrToInt(SL[Index]); Inc(Index); end; .. Insofern macht es schon Sinn, direkt mit Stringlists zu arbeiten. Vielen Dank Euch allen für die Hilfe! (und für mjustin ein :cheers: extra) |
AW: Indy-Test optimieren
Solange man um die Besonderheiten der StringList weiß und die keine Stolperfallen darstellen, kann man die nehmen, wie jedes andere Konstrukt, was sich in einen Stream und wieder zurück wandeln lässt.
Delphi-Quellcode:
sl.Add('test' + sLineBreak + 'data' );
writeln( sl[0] ); // test#13#10data sl.SaveToStream( sm ); sm.Position := 0; sl.LoadFromStream( sm ); writeln( sl[0] ); // test |
AW: Indy-Test optimieren
Um es nochmal zu betonen:
TCP-Verbindungen schnell hintereinander auf und ab zu bauen ist fast immer eine schlechte Idee. Wenn es mit auf- und abbauen übertreibt, kann es sogar passieren, das einem die Ports ausgehen (bei insgesamt wenigen "gleichzeitigen" Verbindungen). Zitat:
Klar kann man ein Protokoll basierend auf UDP haargenau auf seine Anwendung zuschneiden, aber für allgemeine Aufgaben würde ich immer mit TCP beginnen. Natürlich ordentlich weggekapselt hinter einer anwendungsspezifischen Schnittstelle. |
AW: Indy-Test optimieren
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe noch mal einen aktuellen Projektstand angehängt (nur die Quellen).
Wenn man im Client jetzt auf das Formular klickt wird im Server ein Sleep(10Sec) und danach die Drehanweisung ausgeführt. Da der Client die "Datenabfrage" und die "Drehanweisung" aber jetzt in verschiedenen Threads verschickt bleibt der Client bis zum "Berechnungsergebnis" quasi am Leben (zeigt weiter aktuelle Daten an). Ohne getrennte Threads im Client würde der Client natürlich hängen, solange der Server noch nichts zurück schickt - die 10 Sekunden eben. (Ich nutze hier einen anonymen Thread. Mit älteren Delphi-Versionen müsste man das natürlich umstellen.) |
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:56 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz