Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Indy10 Write&ReadStream TArray<String> (https://www.delphipraxis.net/192759-indy10-write-readstream-tarray-string.html)

hzzm 17. Mai 2017 10:44

Indy10 Write&ReadStream TArray<String>
 
Ich versuche, String-arrays dynamischer Laenge deklariert mit TArray<String> mit indy10 in Delphi10Seattle vom Server zum Client schicken zu lassen.
Ich habe mich fuer die Variante mit automatischer StreamLength Uebermittlung entschieden:

Type
Code:
  TTestRecord = TArray<String>;
Server
Code:
procedure TStatusForm.IdTCPUpdateCommands;
var
  S: String;
  I: Integer;
  AStream: TMemoryStream;
  ALength: Integer;
  AContext: TIdContext;
  AClientList: TList;
  ATestRecord: TTestRecord;
begin
  SetLength(ATestRecord, 3);
  ATestREcord[1] := 'Tester';
  if not Assigned(IdTCPServer1.Contexts) then Exit;
  try
    AClientList := IdTCPServer1.Contexts.LockList;
    for I:=0 to AClientList.Count-1 do
    begin
      AContext := TIdContext(AClientList[I]);
      try
        if AContext.Connection.IOHandler.InputBufferIsEmpty then
          AContext.Connection.IOHandler.CheckForDataOnSource(0);
        while not AContext.Connection.IOHandler.InputBufferIsEmpty do
        begin
          S := AContext.Connection.IOHandler.ReadLn;
          if S = 'SendMeTheRecord' then
          begin
            try
              AStream := TMemoryStream.Create;
              ALength := Length(ATestRecord);
              AStream.Position := 0;
              AStream.Write(ALength, SizeOf(Integer));
              AStream.Write(ATestRecord[0], SizeOf(ATestRecord[0])*ALength);
              AContext.Connection.IOHandler.Write(AStream, 0, True); // mit schreiben der Laenge
            finally
              AStream.Free;
            end;
          end;
        end;
      except
        on E: Exception do
          AContext.Connection.Disconnect;
      end;
    end;
  finally
    IdTCPServer1.Contexts.UnlockList;
  end;
end;
Client
Code:
procedure TTransfer2Frame.Button1Click(Sender: TObject);
var
  AStream: TMemoryStream;
  ALength: Integer;
  ASize: Integer;
  ATestRecord: TTestRecord;
begin
  TCP2Frame.IdTCPClient1.IOHandler.WriteLn('SendMeTheRecord');
  try
    AStream := TMemoryStream.Create;
    TCP2Frame.IdTCPClient1.IOHandler.ReadStream(AStream, -1, False); // mit Laengenangabe
    AStream.Position := 0;
    AStream.Read(ALength, SizeOf(Integer));
    SetLength(ATestRecord, ALength);
    AStream.Read(ATestRecord[0], SizeOf(ATestRecord[0])*ALength);
  finally
    AStream.Free;
  end;
end;
Wenn ich mein TestRecord auf TArray<Integer> ummuenze, geht alles. Ein Testwert auf array[1] kommt an.
Mit den Strings verbockt er es aber. Es kommt entweder chinesischer Zeichen-Salat statt "Tester" oder "Nicht verfuegbarer Wert". Warum passiert das und wie kann ich die Situation elegant loesen?

hzzm 17. Mai 2017 12:19

AW: Indy10 Write&ReadStream TArray<String>
 
Hat sich erledigt.
Eigentlich voellig klar:

String ist selbst ein dynamisches array of Char.
Solange man keine fixen Laengen verwendet (TArray<String[30]>), muesste man fuer jeden String individuell die Laenge uebermitteln, wie in meinem Beispiel eine Ebene hoeher fuer Length(TArray<String>) bereits geschehen.

Die ganze Serialisier-Story in delphi habe ich noch nich wirklich befriedigend geloest. Egal ueber welche Streams ich meine Daten jagen will, immer laeuft es darauf hinaus, alles undynamisch zu behandeln. Schade.

Ich kann nicht recht glauben, dass jeder andere Indy-user nur fixed-length Strings uebertraegt.
Oder deren Struktur so simpel ist, dass sie immer nur einen einzelnen String versenden, dessen Laenge sich dann aus dem IOhandler ergibt.

In der Praxis ist es doch zumeist so: Du hast eine ObjectList, die ein dynamisches Array of Record enthaelt, der wiederum...
und dann willst Du den ganzen Kaese ueber TCP/IP uebermitteln.

Ich kann doch nicht der einzige mit dem Problem sein, keinen Bock darauf zu haben, in jeder logischen hierarchie-Ebene zig Laengen von dynamischen Typen einzeln abzugurken.
Wie macht ihr das denn?

mjustin 17. Mai 2017 13:34

AW: Indy10 Write&ReadStream TArray<String>
 
Der Server geht durch alle Connections und schaut ob Nachrichten vom Client empfangen wurden :gruebel:

Macht man das nicht normalerweise in der OnExecute Methode? Diese wird ja im speziellen Threadkontext der Connection ausgeführt, so dass man sich das LockList Gedöns sparen kann und ohne Schleife auskommt.

hzzm 17. Mai 2017 13:57

AW: Indy10 Write&ReadStream TArray<String>
 
Zitat:

Zitat von mjustin (Beitrag 1371755)
Der Server geht durch alle Connections und schaut ob Nachrichten vom Client empfangen wurden :gruebel:

Macht man das nicht normalerweise in der OnExecute Methode? Diese wird ja im speziellen Threadkontext der Connection ausgeführt, so dass man sich das LockList Gedöns sparen kann und ohne Schleife auskommt.

Code:
procedure TStatusForm.IdTCPServer1Execute(AContext: TIdContext);
begin
  IdTCPUpdateCommands;
end;
Ja, stimmt. Wenn ich die Funktion nicht woanders noch ohne AContext aufrufen wuerde, koennte ich mir die Schleife sparen.

mjustin 17. Mai 2017 14:48

AW: Indy10 Write&ReadStream TArray<String>
 
Zitat:

Zitat von hzzm (Beitrag 1371739)
Egal ueber welche Streams ich meine Daten jagen will, immer laeuft es darauf hinaus, alles undynamisch zu behandeln. ... Ich kann nicht recht glauben, dass jeder andere Indy-user nur fixed-length Strings uebertraegt.
Oder deren Struktur so simpel ist, dass sie immer nur einen einzelnen String versenden, dessen Laenge sich dann aus dem IOhandler ergibt.

Läuft es nicht eher auf eine bewährte™ Lösung mittels JSON oder XML hinaus?

Dabei wird alles in einen strukturierten String geschrieben, dieser hat eine bestimmte Länge.

In der Praxis (Protokolle wie AMQP oder MQTT) baut man dann noch einen Block mit Zusatzdaten davor, der aber protokollspezifisch ist und mit bekannten Längen und Trennzeichen arbeitet. Die Payload (Body) ist dann ein Binärblock oder eben ein z.B. nullterminierter String.

hzzm 18. Mai 2017 06:35

AW: Indy10 Write&ReadStream TArray<String>
 
Zitat:

Zitat von mjustin (Beitrag 1371767)
Läuft es nicht eher auf eine bewährte™ Lösung mittels JSON oder XML hinaus?

Wie serialisiere ich denn in Delphi auf die Schnelle?
Ich bin schon eine ganze Weile lang auf der Suche nach einem brauchbaren XML-serializer, finde aber meist schon tote Projekte.

Wenn ich Dich richtig verstehe, sagst du, es gaebe ein Mittel, mit dem ich dynamische Typen (arrays of records of strings o.ae.) einfach nach XML serialisieren kann.

Wie?

TiGü 18. Mai 2017 08:53

AW: Indy10 Write&ReadStream TArray<String>
 
Zitat:

Zitat von hzzm (Beitrag 1371810)
Zitat:

Zitat von mjustin (Beitrag 1371767)
Läuft es nicht eher auf eine bewährte™ Lösung mittels JSON oder XML hinaus?

Wie serialisiere ich denn in Delphi auf die Schnelle?

Mit JSON!
https://community.embarcadero.com/bl...ne-of-code-497


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:53 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz