Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi schnelle Server Client Verbindung ohne Verluste (https://www.delphipraxis.net/216953-schnelle-server-client-verbindung-ohne-verluste.html)

AJ_Oldendorf 28. Mär 2025 07:46

schnelle Server Client Verbindung ohne Verluste
 
Guten Morgen zusammen,
basierend auf diesem Thread, habe ich eine Indy Server und Client Anwendung:
https://www.delphipraxis.net/216803-...nge-tcpip.html

Dabei ist mir allerdings folgendes aufgefallen:

1) erstmal die Aufgabenstellung:
Ich muss vom Server viele Daten zum Client senden und der Client auch genau in der Reihenfolge abarbeiten/verarbeiten.
"Viele Daten" können zwischen 20 und 80 Datensätze (alle unterschiedlich!) pro Sekunde sein. Es dürfen sich keine Datensätze überholen bzw ausfallen. Die Telegramme können zwischen 13 Byte und max 61000 Byte lang sein!
Der Ausfall wird über Connect/Disconnect erkannt.
Dafür wollte ich Indy TCPIP nehmen. Verbindungsaufbau und -abbau klappt soweit, kein Problem.


2) Beobachtungen im Client:
Datensätze werden im Client im einem Thread eingelesen und zwar so:

Delphi-Quellcode:
IdTCPClient1.IOHandler.CheckForDataOnSource(10);

if not IdTCPClient1.IOHandler.InputBufferIsEmpty then
begin
  InData := IdTCPClient1.IOHandler.ReadLn('#~#*' + EOL, 100, -1, IndyTextEncoding_UTF8);

  if InData <> '' then
  begin
    //mach irgendwas damit
  end;
end;
Dabei ist mir aufgefallen, dass der Client die Datensätze nicht in der Geschwindigkeit abholen kann, wie der Server sie mit WriteLn schickt. Auch mit dem Timeout (100) habe ich schon rumgespielt (erhöht, verringert), keine Besserung. Der Client hängt merklich hinterher. Wenn ich im Server das Senden unterdrücke, kann man beim Client richtig zugucken, wie die ReadLn Befehle weiter arbeiten und noch Datensätze abgeholt werden obwohl der Server ja eigentlich nicht mehr schickt (die sind also gepuffert). Das mit dem Puffer ist ja erstmal nicht schlecht, aber der Client kann den Server nie wieder "einholen" wenn der Server aktiv weiterschicken würde. Im Gegenteil, das "hinterherhängen" wird immer schlimmer.
Zur Info, der Server schickt mit WriteLn und Terminator.

3) Frage:
Ist TCPIP dafür überhaupt der richtige Ansatz für meine Aufgabenstellung? Gibt es eine andere Möglichkeit, Daten von einem Server an einem Client in dieser Geschwindigkeit zu schicken und der Client kann diese auch entsprechend verarbeiten? Also wenn der Client mal "bisschen" hinterher hängt, ist ja nicht schlimm aber es darf nicht immer mehr werden. Mit UDP habe ich mal versucht, dass ist natürlich super schnell aber da überholen sich Telegramme und es fallen auch welche aus (ich weiß warum, deswegen fällt UDP raus).

Wie würdet ihr das versuchen zu lösen?

Sinspin 28. Mär 2025 08:01

AW: schnelle Server Client Verbindung ohne Verluste
 
Die Art der Übertragung ist mit Sicherheit nicht das Problem.
Deine Flaschenhälse sind an anderen Stellen.
Zum einen würde ich mir überlegen die Datenpakete zu indexieren damit du rausbekomst ob dazwischen was fehlt.
Weiter musst Du die Verarbeitung vom Empfang trennen. Die Datenpakete müssen also beim Empfang einfach weggespeichert werden und andere Threads kümmern sich ums auswerten.

Wenn das Projekt kommerziell ist, legt dir andere Komponenten für die Übertragung zu. Wir verenden seit vielen Jahren NSoftware. Du musst dich um nichts kümmern, es funktioniert einfach.

Was sind denn eigentlich viele Daten? Bytes pro Sekunde? MB? GB?

AJ_Oldendorf 28. Mär 2025 08:32

AW: schnelle Server Client Verbindung ohne Verluste
 
Zitat:

Was sind denn eigentlich viele Daten? Bytes pro Sekunde? MB? GB?
Die Telegramme können zwischen 13 Byte und max 61000 Byte lang sein.

Ja, die Software ist kommerziell.
NSoftware kenne ich nicht, was ist da "anders/besser"?
Welches Produkt von NSoftware meinst du denn genau?

Die Geschwindigkeitsmessung habe ich auch direkt am WriteLn und ReadLn gemacht. Die Verarbeitung habe ich noch gar nicht geprüft. Das heißt, die Zeit geht ja da verloren bzw man sieht, dass das ReadLn hinterher hängt. Denke nicht, dass es mit der Software drum herum zusammen hängt.

Sinspin 28. Mär 2025 09:23

AW: schnelle Server Client Verbindung ohne Verluste
 
Ich habe mit Indy das letzte mal vor mehr als 10 Jahren gearbeitet. Keine Ahnung ob die jetzt Event basierten Datenempfang haben. (NSoftware macht das sehr schön)
Ich hatte mir das selber geschrieben. Also einen Thread der nichts anderes gemacht hat als immer wieder zu fragen ob was im Eingangspuffer ist, das in einen Zwischenpuffer geschrieben und den immer wieder untersucht bis darin ein vollständiger Datenblock gefunden wurde.
Der Datenblock wurde dann extrahiert und ein Flag gesetzt an dem ein anderer Thread erkannt hat das es was zu tuen gibt.
Kam da mal Müll dazwischen, also ein Datenblock kam nicht vollständig durch, musste der später nochmal angefragt werden.
Wobei ich das mit IP Works jetzt nicht wirklich anders mache. Es läuft nur einfach schön flüssig.

Aber eigentlich sollte es auch mit ReadLn gehen. Es sei denn beim Sender geht das "Ln" nicht raus. Dann wartest du bis zum Timeout für das aktuelle Paket und die dahinter türmen sich bis der interne Puffer voll ist.

Zeig mal deine komplette Config für das Init vom Client. Nicht das nur in der Config was nicht zu dir passt.


NSoftware : IP Works / IP Works SSL.

Papaschlumpf73 28. Mär 2025 09:30

AW: schnelle Server Client Verbindung ohne Verluste
 
Von IPWorks gibts auch eine kostenlos nutzbare (leider auch etwas eingeschränkte) Delphi-Edition über GetIt.

AJ_Oldendorf 28. Mär 2025 09:50

AW: schnelle Server Client Verbindung ohne Verluste
 
NSoftware : IP Works / IP Works SSL muss ich mir mal angucken, auch was es da über GetIt gibt.

Aktuell sieht es so aus:

Server:

Delphi-Quellcode:
IdTCPServer.DefaultPort := PipePort;
IdTCPServer.OnConnect   := IdTCPServerConnected;
IdTCPServer.OnDisconnect := IdTCPServerDisconnected;
IdTCPServer.OnException := IdTCPServerException;
IdTCPServer.OnExecute   := IdTCPServerExecute;
IdTCPServer.Active      := True;

procedure IdTCPServerExecute(AContext: TIdContext);
begin
  if IdTCPServer.Active then
  begin
    AContext.Connection.IOHandler.CheckForDataOnSource(10);

    if not AContext.Connection.IOHandler.InputBufferIsEmpty then
    begin
      InData := AContext.Connection.IOHandler.ReadLn('#~#*' + EOL, 100, -1, IndyTextEncoding_UTF8);
      if InData <> '' then
      begin
        //mach irgendwas
      end;
  end;
end;
Im IdTCPServerConnected, IdTCPServerDisconnected und IdTCPServerException sind nur Protokollierungen. Die sind aber auch nicht zu sehen, kommen also nicht (außer natürlich das 1x Connect, da ein Client sich verbindet).

Client:

Delphi-Quellcode:
IdTCPClient1.OnConnected   := IdTCPClientConnected;
IdTCPClient1.OnDisconnected := IdTCPClientDisconnected;
IdTCPClient1.OnStatus      := IdTCPClientStatus;
IdTCPClient1.Host := aServerIP;
IdTCPClient1.Port := aPort;

procedure TMyThread.Execute;
begin
  while not Terminated do
  begin
    IdTCPClient1.IOHandler.CheckForDataOnSource(10);

    if not IdTCPClient1.IOHandler.InputBufferIsEmpty then
    begin
      InData := IdTCPClient1.IOHandler.ReadLn('#~#*' + EOL, 100, -1, IndyTextEncoding_UTF8);

      if InData <> '' then
      begin
        //mach irgendwas
      end;
  end;
end;
Im IdTCPClientConnected, IdTCPClientDisconnected und IdTCPClientStatus sind nur Protokollierungen. Die sind aber auch nicht zu sehen, kommen also nicht (außer natürlich das 1x Connect, da der Client sich verbindet).

Natürlich ist noch mehr Quelltext drum herum aber das ist die Implementierung von Server und Client.
Wie gesagt, der Server schickt im Durchschnitt 60 Datensätze (unterschiedlicher Inhalt) pro Sekunde mit einer Länge von 13 Byte bis max 61000 Byte

Papaschlumpf73 28. Mär 2025 10:04

AW: schnelle Server Client Verbindung ohne Verluste
 
Stefan meinte wohl das hier im .Execute

Delphi-Quellcode:
      if InData <> '' then
      begin
        //mach irgendwas
      end;
Hier sollten die Daten nur weggespeichert und ggf. durch einen anderen Thread verarbeitet werden. Sonst kann hier ein Flaschenhals entstehen.

fisipjm 28. Mär 2025 10:13

AW: schnelle Server Client Verbindung ohne Verluste
 
Darf ich dazu mal eine "blöde" Frage stellen?
Was für Daten möchtest du denn Übertragen? Was bedeutet denn Datensätze?
Ich würde es mir an deiner Stelle nicht so kompliziert machen. Um Daten von einem Server zu einem Client zu Übertragen schlägt man sich normalerweise nicht mehr mit TCP und dem ganzen geraffel herum.

Best-Practice wäre aus meiner Sicht folgendes:
1.) Auf der Server Seite setzt du dir einen kleinen Webserver auf. Minimale Lösung z.B. Horse. (Bei der Gelegenheit kann man sich auch gleich mal mit Boss auseinandersetzen, vom Grundprinzip sowas wie der Packagemanager nvm nur für Delphi). Wenn du dann auch noch jwt als middleware nutzt, hast du auch gleich noch etwas das sich brauchbar um die Verschlüsslung deiner Kommunikation kümmert.
2.) Deine Datensätze Packst du in Klassen. Wenn du mehrere Datensätze auf einmal Schicken willst packste dir die Klassen in ein Tarray<MyClass> und lässt es danach mit TJSON.ObjectToJSONStr serialisieren. (oder du nimmst gleich irgend ein ORM, oder, oder, oder)


Vorteile:
Aus meiner Sicht kommst du damit locker an die von dir angesprochenen ca. 3,5 MB pro Sekunde (61000Byte x 60(Pakete/s) / 1024 / 1024) hin.
Du brauchst dich nicht mehr um die Reihenfolge kümmern.
Du brauchst dich nicht mehr um das ganzen TCP/IP Connection Zeug kümmern.

Nachteil:
Overhead des HTTP layers, aber wie gesagt, deine Datenrate solltest du damit dicke schaffen.

Ansonsten kannst du die auch mal Websocket zu diesem Thema anschauen. Das ist aber eine ganze Ecke komplizierter.

vG
PJM

Bernhard Geyer 28. Mär 2025 11:08

AW: schnelle Server Client Verbindung ohne Verluste
 
Zitat:

Zitat von Sinspin (Beitrag 1547564)
Zum einen würde ich mir überlegen die Datenpakete zu indexieren damit du rausbekomst ob dazwischen was fehlt.

TCP hat doch selbst eine Prüfung mit Sequenznummer.
Wenn man sowas "selbst in die Hand nimmt", dann würde man auf UDP setzen.
Da gibt es das nicht und muss von der eigenen Anwendung geprüft werden.
Vorteil wäre das man damit das dann auf Geschwindigkeit optimieren kann, wo TCP systembedingt langsamer sein muss.

AJ_Oldendorf 28. Mär 2025 11:10

AW: schnelle Server Client Verbindung ohne Verluste
 
Zitat:

Stefan meinte wohl das hier im .Execute
Also ich habe jetzt jeweils nach dem Senden und Empfang keiner weiteren Aktivitäten mehr, der Client hängt trotzdem hinterher (wesentlich!).

Ein Versuch wäre noch, beides mal wirklich in kleine separate Threads auszulagern, jetzt ist noch ein wenig Overhead drum herum was vielleicht ein Problem sein könnte. Ich werde dazu berichten.

Zitat:

Darf ich dazu mal eine "blöde" Frage stellen?
Also im Endeffekt sind das codierte Strings die dann irgendwelche Commands enthalten und irgendwelche Werte / Signale.

Zitat:

Ich würde es mir an deiner Stelle nicht so kompliziert machen
Naja, das TCPIP Connect/Disconnect geht ja prinzipiell. Dein Vorschlag mir Server und irgendeiner Middleware und JSON etc hört sich aus meiner Sicht erstmal kompliziert an, da davon bei uns aktuell 0 existiert und man sich damit von Grund auf neu beschäftigen müsste. Das andere ist ja da und muss nur implementiert werden. Nicht falsch verstehen, ich bin gerne offen für eine andere Lösung aber wenn dazu erst noch dies und das eingerichtet/gehostet werden muss, ist das nicht praktikabel in unserem Fall. Es soll am Ende nur von einer Windows Anwendung zu einer anderen Windows Anwendung Daten übertragen werden. Diese läuft jeweils auf einem anderen Rechner. Server schickt dem Client was, Client kann dem Server antworten. Das ist das Grundprinzip

Zitat:

Vorteil wäre das man damit das dann auf Geschwindigkeit optimieren kann, wo TCP systembedingt langsamer sein muss.
Genau das habe ich auch schon versucht mit UDP und habe eine Sequenz eingebaut. Wenn man diese mit einbaut, ist es im Prinzip wieder so schnell/langsam wie TCP, da der Client jedes Telegram beantworten muss (damit der Server feststellt, dass es angekommen ist) und der Server muss es wiederholen, falls es vom Client nicht quittiert wurde. Und ohne Sequenz kann man UDP vergessen, da Telegramme verloren gehen können oder sich überholen. Fällt dahe raus

Sinspin 28. Mär 2025 11:16

AW: schnelle Server Client Verbindung ohne Verluste
 
Nen extra Server aufsetzen ist wie Kanonen und Spatzen. Direkt TCPIP is schon die smarteste Lösung für so kleine Sachen.

Zitat:

Zitat von AJ_Oldendorf (Beitrag 1547571)
Aktuell sieht es so aus:

Server:
Delphi-Quellcode:
IdTCPServer.DefaultPort := PipePort;
IdTCPServer.OnConnect := IdTCPServerConnected;
IdTCPServer.OnDisconnect := IdTCPServerDisconnected;
IdTCPServer.OnException := IdTCPServerException;
IdTCPServer.OnExecute := IdTCPServerExecute;
IdTCPServer.Active := True;
<...>

Ich hatte eher gemeint was für Einstellungen für Timeouts, connection handing (keep alive, etc)
Du nimmst die default Einstellungen der Komponente?
Solchen Problemen ist immer schwer beizukommen. Indy Sourcen sind aber mit dabei. Da mal versucht duchzusteppen?
Delphi-Quellcode:
IdTCPClient1.IOHandler.CheckForDataOnSource(10);
<...>
InData := IdTCPClient1.IOHandler.ReadLn('#~#*' + EOL, 100, -1, IndyTextEncoding_UTF8);
<...>
Ich habe jetzt die Quelltexte nicht da um zu prüfen was ReadLn macht, aber eventuell ist die Kombination aus "CheckForDataOnSource" und "ReadLn" das Problem. Was passiert wenn du "CheckForDataOnSource" weglässt?

AJ_Oldendorf 28. Mär 2025 12:19

AW: schnelle Server Client Verbindung ohne Verluste
 
Liste der Anhänge anzeigen (Anzahl: 3)
Zitat:

Ich hatte eher gemeint was für Einstellungen für Timeouts, connection handing (keep alive, etc)
Ich habe gar keine Timeouts oder keep alive gesetzt.
Die Einstellungen der Komponenten sind im Anhang.

Zitat:

Was passiert wenn du "CheckForDataOnSource" weglässt?
Das könnte ich generell mal beim Client versuchen.

Beim Server wurde es im "IdTCPServerExecute" gebraucht, da sonst die CPU Last auf knapp 6% hoch ging. Der Tipp kam von Jaenicke hier https://www.delphipraxis.net/1546974-post25.html

dummzeuch 28. Mär 2025 12:49

AW: schnelle Server Client Verbindung ohne Verluste
 
Mich wundert, dass hier so auf den Indys rumgehackt wird. Ich benutze die seit Jahren ohne Probleme. Wobei ich zugeben muss, dass ich keine großen Datenmengen übertrage und auch nie die Übertragungszeit gemessen habe.

Die Verarbeitung läuft jeweils in Hintergrund-Threads.

Früher war es die Version, die mit Delphi 2007 installiert wurde, inzwischen Indy10 aus dem Github-Repository. Ah ja: Die beteiligten Programme sind in Delphi 2007 und Delphi 10.2 geschrieben.

Bernhard Geyer 28. Mär 2025 12:58

AW: schnelle Server Client Verbindung ohne Verluste
 
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1547577)
Genau das habe ich auch schon versucht mit UDP und habe eine Sequenz eingebaut. Wenn man diese mit einbaut, ist es im Prinzip wieder so schnell/langsam wie TCP, da der Client jedes Telegram beantworten muss (damit der Server feststellt, dass es angekommen ist) und der Server muss es wiederholen, falls es vom Client nicht quittiert wurde. Und ohne Sequenz kann man UDP vergessen, da Telegramme verloren gehen können oder sich überholen. Fällt dahe raus

Wenn der Server ein Packet bestätigt haben will bevor er das nächste abschickt bringt UDP nix.
Der Server muss dann mehrere Packete auf die Reihe schicken und wenn ein Packet nach Zeit x nicht bestätigt wird nochmal senden.
Oder auch der Client bestätigt die Packete "im Block", oder....

Sinspin 28. Mär 2025 15:27

AW: schnelle Server Client Verbindung ohne Verluste
 
Zitat:

Zitat von dummzeuch (Beitrag 1547583)
Mich wundert, dass hier so auf den Indys rumgehackt wird. Ich benutze die seit Jahren ohne Probleme.

Ich höre mal lieber auf, darauf rumzuhacken. Eben mal in die Hilfe geschaut. Das sieht viel besser aus als früher.
Ich habe nur gerade keine Zeit mir mal einen Test zusammenzuschreiben.

Wir betreiben seit Jahren verschiedene Dienste im Firmennetzwerk die via IP Works Komponenten arbeiten. Daher kenne ich die eben am besten.

Um CPU Auslastung zu reduzieren verwende ich
Delphi-Quellcode:
Sleep(0)
. Das ist besser als in irgend einem Teil des Codes Zeit zu verschwenden über den du keine Kontrolle hast.

mjustin 28. Mär 2025 15:47

AW: schnelle Server Client Verbindung ohne Verluste
 
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1547577)
Server schickt dem Client was, Client kann dem Server antworten. Das ist das Grundprinzip

Das ist ein Protokoll das nicht so ganz zu HTTP (siehe Vorschlag weiter vorher) passt. Der Client muss hier nun warten, bis Daten vom Server kommen, diese verarbeiten, und dann wieder warten. (und eventuell eine Antwort zurücksenden).

Dieses Kommunikationsmuster habe ich mit Indy mal in in einem kleinen Demoprogramm als 'umgekehrte request-response' (Anfrage/Antwort) umgesetzt.

https://mikejustin.wordpress.com/202...ocket-library/, Code ist auf https://github.com/michaelJustin/ind...equestResponse

Die Oberfläche erlaubt wahlweise, dass der Client eine Anfrage an den Server sendet, oder umgekehrt, der Server eine Anfrage an den Client sendet.

Es fehlt zwar etwas Fehlerbehandlung - zum Beispiel Prüfung auf ReadLnTimeout nach Readln - aber es zeigt, dass auch der Server die erste(n) Nachricht(en) senden kann, nachdem die Verbindung hergestellt ist.

Der clientseitige Code läuft in einem Thread und sein Betriebsmodus wird durch ein Flag gesteuert. Entweder wird ein Request gesendet und dann auf die Antwort gewartet, oder auf einen Request gewartet und eine Antwort gesendet.

Delphi-Quellcode:
while not Terminated do
begin
  if Send then
  begin
    // send request to server and receive response
    Send := False;
    Request := 'REQ:' + FClientRequestHolder.Text;
    TCPClient.IOHandler.WriteLn(Request);
    repeat
      Response := TCPClient.IOHandler.ReadLn(IndyTextEncoding_UTF8);
    until Response <> '';
    LogInMainThread(Request, Response);
  end
  else
  begin
    // receive request from server and send response
    Request := TCPClient.IOHandler.ReadLn(IndyTextEncoding_UTF8);
    if StartsStr('REQ:', Request) then
    begin
      Response := 'from client ' + ReverseString(Request);
      TCPClient.IOHandler.WriteLn(Response);
    end;
  end;
end;
An diesem Beispiel fällt die Abwesenheit von CheckForDataOnSource auf. Dieses wird nicht benötigt, da ReadLn solange wartet, bis eine Zeile aus dem Socket gelesen wurde, oder es zu einem Timeout kommt.

Kas Ob. 29. Mär 2025 11:44

AW: schnelle Server Client Verbindung ohne Verluste
 
Hi,

@AJ_Oldendorf, there is so much can be written about this, yet don't know where to start, so i will start with what thoughts come to mind (logically ordered or not)
1) You said a lot of data between 20 and 80 packet, ( assuming packet and telegram are the same with in the translation !), see i wrote many servers and they are tested and running in real production, working with more than 400k packet per second saturating almost %50 of the 2.5Gbps connection, yet the CPU never goes above %6, some of traffic is done on MySQL, simple insert and delete but mostly update,
so can TCP solve you need ?, absolutely yes.

2) In older thread you tried to get the MTU and its 1500b effect on the speed, before you go that way, i suggest lets fix you problem by understanding it, which i will try below, but going after low level is white rabbit hole, as yes it is 1500 most the time, yet it will never be like that as this is isolated by the network adapter/driver, and every OS has many option/setting that will affect this behavior, yet it will stay 1500, but you user mode software will handle it differently, i suggest to not care about these deep and low level for now, as they are not your problem, forget about tweaking low level stuff for now, don't waste your time while you problem is in different place.

3) Indy is not slow or slower of any other, all other are exactly like that, the same, all of them will serve you at the same speed, the difference will manifested in aspects you will not need, not in this case/scenario, so no matter what you will use it will serve you well.

4) Once i saw ReadLn and WriteLn, i know for sure this can't perform s***, the server will send line by line and the client will read line by line, this is a huge bottleneck in the code.

5) I see this code
Delphi-Quellcode:
IdTCPClient1.IOHandler.CheckForDataOnSource(10);

if not IdTCPClient1.IOHandler.InputBufferIsEmpty then
begin
  InData := IdTCPClient1.IOHandler.ReadLn(' #~#* ' + EOL, 100, -1, IndyTextEncoding_UTF8);

  if InData <> '' then
  begin
    //do something with it
  end ;
end ;
and i know for sure this can't be perform (like 4), mixing lines with process in place, you can do this only if you send data very rarely, here you combined most worse approaches in one loop.

What is happening here ? let me give you an example how these act as sticks in wheel:
1) Server send line after line, here Nagle (if enabled) will matter, but in not much as will see below.
2) Client notified to receive
3) Client Read One Single Line, in most and slowest possible way, that handle char by char.
4) Client proceed to process One Single Line.
5) When client extracted one line from the received buffer .. well this part is important, the extraction happened in a buffer in user space owned by you own process because you received it, it could be one line or more than one, it could be one or more MTU splitting many lines, well this fact is not important as the important part is what is happening on the network at this exact moment, when you application issued read line for the first time, read all what it could from the network using API like recv, Indy with ReadLn will only process and grab from the network new recv if there is no new lines in the already received and memory loaded buffers, so in other words and shorter way...
When you are handling line by line the network buffers could be filled from the peer (server here) and this will trigger stop sending ACK, so the server will stop sending lines/buffers, your application handle more and more lines and then request one buffer no matter how much in size, it will read from the network driver/provider..., triggering sending an ACK to server server will send one and wait new ACK, and you client server now locked in very slow recv-process-request, extremely slow.

Suggestions:
1) start with removing WriteLn/ReadLn with something useful and performant, in fact, any other method is faster.
2) Send the data in bulk as much as you can.
3) Read as much as you can on each and every read from the socket, provide a buffer lets say 16kb buffer perform read for 16kb or 64kb, even 8kb will be fast enough as it is default for most applications, the point is when read is needed read what you can and as much as you can, not as much you need.
4) Try ICS may be you will like it, there is an obsession with old and outdated practices like this case with readln/writeln, heck.. i was shocked that TFile is still used by many, it is slow as turtle, use modern practices.
5) I saw another code above like this one by mjustin
Delphi-Quellcode:
while not Terminated do
begin
  if Send then
  begin
    // send request to server and receive response
    Send := False;
    Request := ' REQ: ' + FClientRequestHolder.Text;
    TCPClient.IOHandler.WriteLn(Request);
    repeat
      Response := TCPClient.IOHandler.ReadLn(IndyTextEncoding_UTF8);
    until Response <> '';
    LogInMainThread(Request, Response);
  end
  else
  begin
    // receive request from server and send response
    Request := TCPClient.IOHandler.ReadLn(IndyTextEncoding_UTF8);
    if StartsStr(' REQ: ', Request) then
    begin
      Response := ' from client ' + ReverseString(Request);
      TCPClient.IOHandler.WriteLn(Response);
    end ;
  end ;
end ;
Will comment on that one line "LogInMainThread(Request, Response);" what do you think the network stream behavior will be while the application is synchronizing with main thread, i explained above, it trigger full stop send on server, depleted socket window for this socket and many others, lost the sending momentum that build up with TCP between peers(unlike UDP which start with maximum), this behavior you see when downloading huge files, the first few seconds the speed is low then gradually build up to saturate the allowed bandwidth, as the server start to send more and more without waiting for ACK.

Hope that helps.

jaenicke 30. Mär 2025 23:51

AW: schnelle Server Client Verbindung ohne Verluste
 
Wenn es so schnell sein muss, sollten die Daten sofort an einen anderen Thread zur Verarbeitung gegeben werden und dort erst die Pakete extrahiert werden. So bekomme ich eine sehr geringe Latenz. Client:
Delphi-Quellcode:
program Project204;

uses
  System.Classes, System.SysUtils, IdTCPClient, IdGlobal;

type
  TMyTCPClient = class
  private
    FClient: TIdTCPClient;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Connect(const AHost: string; APort: Integer);
    procedure Disconnect;
    procedure SendData(const Data: TIdBytes);
  end;

{ TMyTCPClient }

constructor TMyTCPClient.Create;
begin
  FClient := TIdTCPClient.Create(nil);
end;

destructor TMyTCPClient.Destroy;
begin
  Disconnect;
  FreeAndNil(FClient);
  inherited;
end;

procedure TMyTCPClient.Connect(const AHost: string; APort: Integer);
begin
  FClient.Host := AHost;
  FClient.Port := APort;
  FClient.ConnectTimeout := 5000; // 5 Sekunden Timeout
  FClient.ReadTimeout := 5000;   // 5 Sekunden Timeout für Lesevorgänge
  FClient.Connect;
  Writeln('Verbunden mit ', AHost, ':', APort);
end;

procedure TMyTCPClient.Disconnect;
begin
  if FClient.Connected then
  begin
    FClient.Disconnect;
    Writeln('Verbindung getrennt.');
  end;
end;

procedure TMyTCPClient.SendData(const Data: TIdBytes);
begin
  if FClient.Connected then
  begin
    FClient.IOHandler.Write(Data);
    Writeln(Now, 'Gesendet: ', Length(Data), ' Bytes');
  end
  else
    Writeln('Fehler: Nicht verbunden.');
end;

var
  MyClient: TMyTCPClient;
  TestData: TIdBytes;
begin
  try
    MyClient := TMyTCPClient.Create;
    try
      MyClient.Connect('127.0.0.1', 5000);

      SetLength(TestData, 1024);
      FillChar(TestData[0], Length(TestData), 65);

       for var i := 1 to 1000000 do
        MyClient.SendData(TestData);

      Readln;
      MyClient.Disconnect;
    finally
      FreeAndNil(MyClient);
    end;
  except
    on E: Exception do
      Writeln('Fehler: ', E.Message);
  end;
end.
Server:
Delphi-Quellcode:
program Project203;

uses
  System.Classes, System.SysUtils, System.SyncObjs, IdTCPServer, IdContext, IdGlobal, System.Generics.Collections;

type
  TDataQueue = class
  private
    FQueue: TQueue<TIdBytes>;
    FLock: TCriticalSection;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Enqueue(const Data: TIdBytes);
    function Dequeue: TIdBytes;
  end;

  TProcessingThread = class(TThread)
  private
    FDataQueue: TDataQueue;
  protected
    procedure Execute; override;
  public
    constructor Create(ADataQueue: TDataQueue);
  end;

  TMyTCPServer = class
  private
    FServer: TIdTCPServer;
    FDataQueue: TDataQueue;
    FProcessingThread: TProcessingThread;
    procedure OnExecuteHandler(AContext: TIdContext);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Start;
    procedure Stop;
  end;

{ TDataQueue }

constructor TDataQueue.Create;
begin
  FQueue := TQueue<TIdBytes>.Create;
  FLock := TCriticalSection.Create;
end;

destructor TDataQueue.Destroy;
begin
  FQueue.Free;
  FLock.Free;
  inherited;
end;

procedure TDataQueue.Enqueue(const Data: TIdBytes);
begin
  FLock.Acquire;
  try
    FQueue.Enqueue(Data);
  finally
    FLock.Release;
  end;
end;

function TDataQueue.Dequeue: TIdBytes;
begin
  FLock.Acquire;
  try
    if FQueue.Count > 0 then
      Result := FQueue.Dequeue
    else
      SetLength(Result, 0);
  finally
    FLock.Release;
  end;
end;

{ TProcessingThread }

constructor TProcessingThread.Create(ADataQueue: TDataQueue);
begin
  FDataQueue := ADataQueue;
  inherited Create(False);
end;

procedure TProcessingThread.Execute;
var
  Data: TIdBytes;
begin
  while not Terminated do
  begin
    Data := FDataQueue.Dequeue;
    if Length(Data) > 0 then
    begin
      Writeln('Empfangen: ', Length(Data), ' Bytes');
    end
    else
      Sleep(1);
  end;
end;

{ TMyTCPServer }

constructor TMyTCPServer.Create;
begin
  FDataQueue := TDataQueue.Create;
  FProcessingThread := TProcessingThread.Create(FDataQueue);
  FServer := TIdTCPServer.Create(nil);
  FServer.DefaultPort := 5000;
  FServer.OnExecute := OnExecuteHandler;
end;

destructor TMyTCPServer.Destroy;
begin
  Stop;
  FreeAndNil(FServer);
  FreeAndNil(FProcessingThread);
  FreeAndNil(FDataQueue);
  inherited;
end;

procedure TMyTCPServer.OnExecuteHandler(AContext: TIdContext);
var
  Buffer: TIdBytes;
begin
  SetLength(Buffer, 8192);
  while AContext.Connection.IOHandler.InputBuffer.Size > 0 do
  begin
    AContext.Connection.IOHandler.ReadBytes(Buffer, Length(Buffer), False);
    FDataQueue.Enqueue(Buffer);
  end;
end;

procedure TMyTCPServer.Start;
begin
  FServer.Active := True;
end;

procedure TMyTCPServer.Stop;
begin
  FServer.Active := False;
end;

var
  MyServer: TMyTCPServer;

begin
  try
    MyServer := TMyTCPServer.Create;
    MyServer.Start;
    Writeln('Server läuft auf Port 5000. Drücke Enter zum Beenden.');
    Readln;
    MyServer.Stop;
    FreeAndNil(MyServer);
  except
    on E: Exception do
      Writeln('Fehler: ', E.Message);
  end;
end.

AJ_Oldendorf 31. Mär 2025 05:29

AW: schnelle Server Client Verbindung ohne Verluste
 
Danke jaenicke und danke an Kas Ob.

Ich habe verstanden, dass TCPIP und Indy mein Grundprinzip locker abdecken können. Ich darf nur nicht mit ReadLn und WriteLn arbeiten. Ich werde versuchen, meine Software umzugestalten (auf getrenntes Senden und Empfangen in Threads) sowie ohne Terminator und TIdBytes sowie immer Maximum lesen. Ich werde berichten...

DenkDirNix 31. Mär 2025 17:39

AW: schnelle Server Client Verbindung ohne Verluste
 
Zitat:

Zitat von jaenicke (Beitrag 1547639)
Wenn es so schnell sein muss, sollten die Daten sofort an einen anderen Thread zur Verarbeitung gegeben werden und dort erst die Pakete extrahiert werden. So bekomme ich eine sehr geringe Latenz.

Ich habe Deinen Code mal ausprobiert, der Übersichtlichkeit halber sendet mein Client nur 10 Pakete à 1024 Bytes. Der Server empfängt aber nur einmal 8192 Bytes und bleibt dann hängen. Kann da ein Fehler im OnExecuteHandler des Servers vorliegen? Dies ist unabhängig von der Verwendung der Queue.

jaenicke 31. Mär 2025 21:51

AW: schnelle Server Client Verbindung ohne Verluste
 
Bei mir läuft das so 1:1 durch. Wenn das bei dir hängt, ist die Frage, wo der Server dann hängt. Da es bei mir nicht passiert, tappe ich da im Dunkeln, da müsstest du mal im Debugger schauen...

DenkDirNix 1. Apr 2025 09:10

AW: schnelle Server Client Verbindung ohne Verluste
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von jaenicke (Beitrag 1547668)
Bei mir läuft das so 1:1 durch. Wenn das bei dir hängt, ist die Frage, wo der Server dann hängt. Da es bei mir nicht passiert, tappe ich da im Dunkeln, da müsstest du mal im Debugger schauen...

Meine Änderung an Deinem Code ist wie gesagt nur die, dass der Client nur 10 mal 1024 Bytes schickt.

Anbei mal der Stack (kann man den aus der IDE auch direkt als Text speichern ???).
Im OnExecuteHandler werden im ersten Durchlauf 8192 von den vorliegenden 10240 Bytes abgeholt, der zweite Durchlauf für die restlichen 2048 Bytes führt dann zum gezeigten Stack so weit ich ihn verfolgt habe. Leider führt der Befehl "Pause" für einen hängenden Prozess ja nur zu einem leeren Stack.

Der etwas irritierende Parameterwert "-2" im ReadFromSource() ist nicht der Original-Aufrufparameter sondern der vom Code dann auf "IdTimeoutInfinite" geänderte.

jaenicke 1. Apr 2025 10:18

AW: schnelle Server Client Verbindung ohne Verluste
 
Ach so, ja, das ist Absicht. Es ging ja um Geschwindigkeitstest, da habe ich den Fall, dass ein Paket oder eine Gruppe von Paketen (8192 Byte) nicht vollständig ankommt, nicht behandelt.

MyRealName 1. Apr 2025 11:50

AW: schnelle Server Client Verbindung ohne Verluste
 
Ich habe früher viel RealThinClient genutzt, auch mittlerweile OpenSource. Ist sehr stabil, schnell und einfach zu implementieren

Kas Ob. 1. Apr 2025 15:26

AW: schnelle Server Client Verbindung ohne Verluste
 
Zitat:

Zitat von MyRealName (Beitrag 1547700)
Ich habe früher viel RealThinClient genutzt, auch mittlerweile OpenSource. Ist sehr stabil, schnell und einfach zu implementieren

Good choice and great advice, as RTC is the best of them all !

I either do low level using API for extra tweaking or RTC for anything else, also notice with RTC, you don't need to use its components and classes from the palate, but can use the RAW ones for TCP and UDP, they are powerful and comes with threading (pool and sync... etc) out-of-the-box that can be switched from Multithreaded (asynchronous) to Single-thread (synchronous) by flipping a bool.

mjustin 1. Apr 2025 16:35

AW: schnelle Server Client Verbindung ohne Verluste
 
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1547641)
Ich darf nur nicht mit ReadLn und WriteLn arbeiten.

Mit Indy darf man auch ReadLn und WriteLn verwenden, ohne Performanceproblem.

Indy verwendet einen 32K (default) großen Buffer für das Empfangen von Daten. Man kann die Buffergröße bei Bedarf auch noch erhöhen (siehe TIdIOHandler.SendBufferSize property). Auch beim Senden wird ein 32k (default) großer Buffer verwendet.

Dass IOHandler.ReadLn / WriteLn schlechtere Performance hat, wäre schon vor Jahren Thema in den einschlägigen Delphi-Foren gewesen, wenn es denn wahr wäre.

AJ_Oldendorf 2. Apr 2025 09:56

AW: schnelle Server Client Verbindung ohne Verluste
 
@jaenicke
Ich habe dein Code mal minimal angepasst gemäß deinem Beitrag hier:

https://www.delphipraxis.net/1547639-post18.html

Server:

Delphi-Quellcode:
program Server;

uses
  System.Classes,
  System.SysUtils,
  System.SyncObjs,
  IdTCPServer,
  IdContext,
  IdGlobal,
  System.Generics.Collections,
  System.Diagnostics;

type
  TDataQueue = class
  private
    FQueue: TQueue<TIdBytes>;
    FLock: TCriticalSection;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Enqueue(const Data: TIdBytes);
    function Dequeue: TIdBytes;
  end;

  TProcessingThread = class(TThread)
  private
    FDataQueue: TDataQueue;

    Anz : Integer;
  protected
    procedure Execute; override;
  public
    constructor Create(ADataQueue: TDataQueue);
  end;

  TMyTCPServer = class
  private
    FServer: TIdTCPServer;
    FDataQueue: TDataQueue;
    FProcessingThread: TProcessingThread;
    procedure OnExecuteHandler(AContext: TIdContext);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Start;
    procedure Stop;
  end;

{ TDataQueue }

constructor TDataQueue.Create;
begin
  FQueue := TQueue<TIdBytes>.Create;
  FLock := TCriticalSection.Create;
end;

destructor TDataQueue.Destroy;
begin
  FQueue.Free;
  FLock.Free;
  inherited;
end;

procedure TDataQueue.Enqueue(const Data: TIdBytes);
begin
  FLock.Acquire;
  try
    FQueue.Enqueue(Data);
  finally
    FLock.Release;
  end;
end;

function TDataQueue.Dequeue: TIdBytes;
begin
  FLock.Acquire;
  try
    if FQueue.Count > 0 then
      Result := FQueue.Dequeue
    else
      SetLength(Result, 0);
  finally
    FLock.Release;
  end;
end;

{ TProcessingThread }

constructor TProcessingThread.Create(ADataQueue: TDataQueue);
begin
  FDataQueue := ADataQueue;
  Anz := 0;
  inherited Create(False);
end;

procedure TProcessingThread.Execute;
var
  Data: TIdBytes;
  sw3 : TStopwatch;
  t3 : Int64;
begin
  while not Terminated do
  begin
    Data := FDataQueue.Dequeue;
    if Length(Data) > 0 then
    begin
      Inc(Anz, Length(Data));

      Writeln('Empfangen: ', Length(Data), ' Bytes' + '- Anz: ' + Anz.ToString);

      Writeln('Gesamtlänge: ' + Anz.ToString + ' Bytes');
    end
    else
      Sleep(1);
  end;
end;

{ TMyTCPServer }

constructor TMyTCPServer.Create;
begin
  FDataQueue := TDataQueue.Create;
  FProcessingThread := TProcessingThread.Create(FDataQueue);
  FServer := TIdTCPServer.Create(nil);
  FServer.DefaultPort := 5000;
  FServer.OnExecute := OnExecuteHandler;
end;

destructor TMyTCPServer.Destroy;
begin
  Stop;
  FreeAndNil(FServer);
  FreeAndNil(FProcessingThread);
  FreeAndNil(FDataQueue);
  inherited;
end;

procedure TMyTCPServer.OnExecuteHandler(AContext: TIdContext);
var
  Buffer: TIdBytes;
begin
  //SetLength(Buffer, 61000); //<- nicht feste größe einlesen
  while AContext.Connection.IOHandler.InputBuffer.Size > 0 do
  begin
    SetLength(Buffer, AContext.Connection.IOHandler.InputBuffer.Size); //<- so viel einlesen wie im Buffer enthalten ist
    AContext.Connection.IOHandler.ReadBytes(Buffer, Length(Buffer), False);
    FDataQueue.Enqueue(Buffer);
  end;
end;

procedure TMyTCPServer.Start;
begin
  FServer.Active := True;
end;

procedure TMyTCPServer.Stop;
begin
  FServer.Active := False;
end;

var
  MyServer: TMyTCPServer;

begin
  try
    MyServer := TMyTCPServer.Create;
    MyServer.Start;
    Writeln('Server läuft auf Port 5000. Drücke Enter zum Beenden.');
    Readln;
    MyServer.Stop;
    FreeAndNil(MyServer);
  except
    on E: Exception do
      Writeln('Fehler: ', E.Message);
  end;
end.
Client:

Delphi-Quellcode:
program Client;

uses
  System.Classes,
  System.SysUtils,
  IdTCPClient,
  IdGlobal,
  System.Diagnostics;

type
  TMyTCPClient = class
  private
    FClient: TIdTCPClient;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Connect(const AHost: string; APort: Integer);
    procedure Disconnect;
    procedure SendData(const Data: TIdBytes);
  end;

{ TMyTCPClient }

constructor TMyTCPClient.Create;
begin
  FClient := TIdTCPClient.Create(nil);
end;

destructor TMyTCPClient.Destroy;
begin
  Disconnect;
  FreeAndNil(FClient);
  inherited;
end;

procedure TMyTCPClient.Connect(const AHost: string; APort: Integer);
begin
  FClient.Host := AHost;
  FClient.Port := APort;
  FClient.ConnectTimeout := 5000; // 5 Sekunden Timeout
  FClient.ReadTimeout := 5000; // 5 Sekunden Timeout für Lesevorgänge
  FClient.Connect;
  //FClient.IOHandler.RecvBufferSize := 32768;
  //FClient.IOHandler.SendBufferSize := 32768;
  //FClient.IOHandler.RecvBufferSize := 61440;
  //FClient.IOHandler.SendBufferSize := 61440;
  Writeln('Verbunden mit ', AHost, ':', APort);
end;

procedure TMyTCPClient.Disconnect;
begin
  if FClient.Connected then
  begin
    FClient.Disconnect;
    Writeln('Verbindung getrennt.');
  end;
end;

procedure TMyTCPClient.SendData(const Data: TIdBytes);
begin
  if FClient.Connected then
  begin
    FClient.IOHandler.Write(Data);
    //Writeln(Now, ' Gesendet: ', Length(Data), ' Bytes');
  end
  else
  begin
    Writeln('Fehler: Nicht verbunden.');
  end;
end;

var
  MyClient: TMyTCPClient;
  TestData: TIdBytes;
  Anz : LongWord;
begin
  try
    MyClient := TMyTCPClient.Create;
    try
      MyClient.Connect('127.0.0.1', 5000);

      var sw3 := TStopwatch.StartNew;
      var t3 : Int64;

      SetLength(TestData, 61000); //1024
      FillChar(TestData[0], Length(TestData), 65);

      Anz := 0;

      for var i := 1 to 20 do
      begin
        Inc(Anz, Length(TestData));

        MyClient.SendData(TestData);
      end;

      t3 := sw3.ElapsedMilliseconds; //Zeitmessung stoppen
      Writeln('Zeitdauer: ' + t3.ToString + ' ms');

      Writeln('Gesamtlänge: ' + Anz.ToString + ' Bytes');

      Readln;
      MyClient.Disconnect;
    finally
      FreeAndNil(MyClient);
    end;
  except
    on E: Exception do
      Writeln('Fehler: ', E.Message);
  end;
end.
Folgendes Erscheinungsbild:

1) Client sendet mit 20 Telegrammen in Summe 1220000 Bytes, wird auch angezeigt
2) Server empfängt in 38 Telegrammen die 1220000 Bytes, wird auch angezeigt
3) CPU Auslastung vom Server steigt danach auf 5-7% ohne das weitere Daten empfangen werden oder vom Client geschickt werden

Beim OnExecuteHandler vom Server habe ich eine kleine Anpassung gemacht (siehe Kommentar).

Frage:
Woran liegt das mit der CPU Auslastung und wie bekommt man das gelöst?
Müsste der Server nicht auch 20 Telegramme á 61000 Bytes empfangen, anstatt 38 Stück?

AJ_Oldendorf 3. Apr 2025 09:06

AW: schnelle Server Client Verbindung ohne Verluste
 
@jaenicke
Ich push einfach mal. Hast du dazu eine Lösung/Idee?

Es ist ja so, dass mein Client immer unterschiedlich lange Telegramme schickt.
Die können von 13 Byte bis 61000 Byte lang sein. Damit muss der Server klar kommen und ich dachte, ich kann beim Server dann entsprechend so viel Byte einlesen, wie der InputBuffer lang ist.

Kas Ob. 3. Apr 2025 10:02

AW: schnelle Server Client Verbindung ohne Verluste
 
@AJ_Oldendorf i am no expert in Indy at all, as i hate the s*** out of it due to exception raising policy, but while waiting for someone to resolve this for you, let me put few thoughts

1) Make sure you are blocking on the socket means as long you existing the loop then it will return and this will raise the CPU usage up to full Thread/Core.
2) ReadLn is blocking capable and that why it used everywhere.
3) Guess what ?! ReadBytes also blocking capable too.

so try this
Delphi-Quellcode:
procedure TMyTCPServer.OnExecuteHandler(AContext: TIdContext);
var
  Buffer: TIdBytes;
begin
  if not AContext.Connection.Connected then
    Exit;

  AContext.Connection.IOHandler.ReadBytes(Buffer, -1, True); // block and wait ! , while True for append (best practice) in case there is leftover

  if Length(Buffer) > 0 then
    FDataQueue.Enqueue(Buffer);
  {
  //SetLength(Buffer, 61000); //<- nicht feste größe einlesen
  while not AContext.Connection.IOHandler.InputBufferIsEmpty do
  begin
    SetLength(Buffer, AContext.Connection.IOHandler.InputBuffer.Size); //<- so viel einlesen wie im Buffer enthalten ist
    AContext.Connection.IOHandler.ReadBytes(Buffer, Length(Buffer), False);
    FDataQueue.Enqueue(Buffer);
  end;}
end;
On side note this blocking might need timeout adjustment, so adjust AContext.Connection.IOHandler.ReadTimeout to something short, it could be 30 second or even shorter will be fine, this might affect how much concurrent connection you expect, if few then put it 1 second (timeout = 1000) and it will be find.

about the hate for Indy, it comes form where an exception will surprise you, well from almost every line/operation, so i think some try..except is due somewhere in your client and server, but again you need someone else to help with what i wrote (which could be not the optimal) and exception handling..... or
Try somethin else ICS has plenty of examples/samples and it is way more friendly with its events, Indy still valuable and will stay the most protocol stuffed library in pascal.

AJ_Oldendorf 3. Apr 2025 10:28

AW: schnelle Server Client Verbindung ohne Verluste
 
ok, ich glaube es verstanden zu haben

Delphi-Quellcode:
while AContext.Connection.IOHandler.InputBuffer.Size > 0 do
begin
  SetLength(Buffer, AContext.Connection.IOHandler.InputBuffer.Size);
  AContext.Connection.IOHandler.ReadBytes(Buffer, Length(Buffer), False);

  FDataQueue.Enqueue(Buffer);
end;
Sleep(1);
Durch das Sleep(1) funktioniert es auch (dass die CPU Auslastung nicht ansteigt), wenn kein Empfang mehr stattfindet.
Nur ist mir nicht klar, warum der Client 20 Telegramme á 61000 Byte abschickt und der Server 38 Stück empfängt wobei die meisten 32768 Byte lang sind und am Ende ein kürzeres. Warum nicht auch 20 Telegramme?

TomyN 3. Apr 2025 10:45

AW: schnelle Server Client Verbindung ohne Verluste
 
Evtl. 32k Buffersize?

jaenicke 3. Apr 2025 10:48

AW: schnelle Server Client Verbindung ohne Verluste
 
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1547776)
Durch das Sleep(1) funktioniert es auch (dass die CPU Auslastung nicht ansteigt), wenn kein Empfang mehr stattfindet.

Besser ist es, wenn du das Sleep nur ausführst, wenn keine Daten vorhanden waren. Dadurch verzögerst du den Empfang nicht, wenn Daten kommen.
Delphi-Quellcode:
procedure TMyTCPServer.OnExecuteHandler(AContext: TIdContext);
var
  Buffer: TIdBytes;
begin
  if AContext.Connection.IOHandler.InputBuffer.Size > 0 then
  begin
    while AContext.Connection.IOHandler.InputBuffer.Size > 0 do
    begin
      SetLength(Buffer, AContext.Connection.IOHandler.InputBuffer.Size); //<- so viel einlesen wie im Buffer enthalten ist
      AContext.Connection.IOHandler.ReadBytes(Buffer, Length(Buffer), False);
      FDataQueue.Enqueue(Buffer);
    end;
  end
  else
    Sleep(1);
end;
Zitat:

Zitat von AJ_Oldendorf (Beitrag 1547776)
Nur ist mir nicht klar, warum der Client 20 Telegramme á 61000 Byte abschickt und der Server 38 Stück empfängt wobei die meisten 32768 Byte lang sind und am Ende ein kürzeres. Warum nicht auch 20 Telegramme?

Darauf hast du keinen direkten Einfluss. Die Pakete werden für den Transport nach Bedarf unterteilt und beim Empfang in den Puffer geschrieben. Man kann dort zwar auch Einstellungen setzen, aber das garantiert dennoch nicht ein bestimmtes Verhalten.

AJ_Oldendorf 3. Apr 2025 11:12

AW: schnelle Server Client Verbindung ohne Verluste
 
@jaenicke: Danke für den Hinweis mit dem Sleep.
Beim Empfang muss ich sowieso die Telegramme wieder entsprechend zusammensetzen. Wollte es nur verstanden haben.

EDIT:
Das mit dem Sleep in deiner Variante funktioniert leider nicht. Da geht die CPU Auslastung auch auf 7% hoch nachdem der Empfang fertig ist

jaenicke 3. Apr 2025 18:23

AW: schnelle Server Client Verbindung ohne Verluste
 
Ach, Entschuldigung, das ist ja auch verkehrt herum. Ich habe es korrigiert. Die Beschreibung war korrekt, der Quelltext nicht. Das Sleep muss ins Else.

AJ_Oldendorf 4. Apr 2025 05:33

AW: schnelle Server Client Verbindung ohne Verluste
 
Ich danke dir :-)

AJ_Oldendorf 4. Apr 2025 11:14

AW: schnelle Server Client Verbindung ohne Verluste
 
@jaenicke:
Ich muss doch nochmal fragen, wie würde in deinem Beispiel denn die Implementierung aussehen, wenn der Client auch empfangen kann auf Telegramme vom Server? Da gibt es die
Delphi-Quellcode:
procedure OnExecuteHandler
ja nicht also müsste man zyklisch das pollen über einen Thread machen oder?

jaenicke 4. Apr 2025 19:49

AW: schnelle Server Client Verbindung ohne Verluste
 
Ja, das kannst du mit einem Thread machen, der pollt. Aber du musst dann auch sicherstellen, dass nicht gleichzeitig gelesen und geschrieben wird, sprich einen entsprechenden Lock verwenden.

Ich finde diese Architektur nicht schön, aber es funktioniert.

AJ_Oldendorf 7. Apr 2025 08:27

AW: schnelle Server Client Verbindung ohne Verluste
 
@jaenicke:

Hm, ein Thread, der zyklisch liest (wie in deinem Beispiel) und ein Thread, der zyklisch schreibt, geht nicht?
Ich muss die beiden untereinander wieder synchronisieren? Irgendwie habe ich erwartet, dass das die Indy intern machen :-(

jaenicke 7. Apr 2025 09:38

AW: schnelle Server Client Verbindung ohne Verluste
 
Du kannst z.B. das reine Senden und Empfangen mit TMonitor absichern:
Delphi-Quellcode:
TMonitor.Enter(AContext.Connection.IOHandler);
try
  AContext.Connection.IOHandler.ReadBytes...
finally
  TMonitor.Exit(AContext.Connection.IOHandler);
end;

AJ_Oldendorf 7. Apr 2025 11:13

AW: schnelle Server Client Verbindung ohne Verluste
 
Also beim Server, habe ich das Senden in einem Thread jetzt so ausgelagert:

Delphi-Quellcode:
var
  SendMsg : PAnsiString;
  Daten   : AnsiString;
  outMsg  : TIdBytes;

...
if ClientContext.Connection.Connected then
begin
  while (Sendeliste.Count > 0) do
  begin
    var sw4 := TStopwatch.StartNew;
    var t4 : Int64;

    SendMsg := Sendeliste.Items[0];
    Daten := SendMsg^;

    SetLength(outMsg, Length(Daten));
    Move(Daten[1], outMsg[0], Length(Daten));

    t4 := sw4.ElapsedMilliseconds; //Zeitmessung stoppen
    //Zeitmessung 01 auswerten

    sw4 := TStopwatch.StartNew; //Zeitmessung starten

    ClientContext.Connection.IOHandler.Write(outMsg);

    t4 := sw4.ElapsedMilliseconds; //Zeitmessung stoppen
    //Zeitmessung 02 auswerten

    sw4 := TStopwatch.StartNew; //Zeitmessung starten

    Dispose(SendMsg);
    Sendeliste.Delete(0);

    t4 := sw4.ElapsedMilliseconds; //Zeitmessung stoppen
    //Zeitmessung 03 auswerten
  end;
end
Zeitmessung 02 schlägt regelmäßig zu, 300ms ist normal (pro Aufruf), manchmal sogar länger.
Hast du eine Idee dazu?

Das ReadEvent habe ich über ein Merker geblockt, sodass in dem Moment kein Schreiben möglich ist. Da findet also in dem Moment nichts statt.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:55 Uhr.
Seite 1 von 2  1 2      

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