Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Stringlist mit SendStream versenden (https://www.delphipraxis.net/191696-stringlist-mit-sendstream-versenden.html)

runningsoft 10. Feb 2017 22:59

Stringlist mit SendStream versenden
 
Hallo zusammen,

ich sitze schon ewig über der Aufgabe einen Record, der neben einigen festen Strings und Integerwerten auch eine Stringlist enthält, über TClientSocket bzw. TServerSocket zu versenden bzw. zu empfangen. Leider bisher ohne Erfolg. Lasse ich die Stringliste testweise aus meinem Record heraus, funktioniert das versenden des Records vom Client an den Server wunderbar. Versende ich das ganze mit der Stringliste gibts am Server sofort eine Exception, sobald ich versuche auf den Inhalt der übertragenen Stringliste zuzugreifen.

Hier mal ein bisschen Code:
Delphi-Quellcode:
type
CustomRec = record
   RecID : Integer;
   RecVorgang : String[15];
   RecStatus: integer;
   ....
   RecTNListe: TStringList;
   end;
Erstellen und versenden des Records am Client:
Delphi-Quellcode:
procedure TClientForm.BitBtn1Click(Sender: TObject);
var MyTNList : TStringList;
    MStr : TMemoryStream;

begin
....
Finalize(Rec); //Record zur Sicherheit komplett leeren
Rec.RecListe := TStringList.Create;    //Stringliste des Records erzeugen
with Rec do
   begin
   RecID := GetClientID;
   RecVorgang := 'Irgendwas';
   RecStatus := 2;
   .....
   RecTNListe.Text := MyTNList.Text;   //MyTNList wurde vorher created und enthält Daten
   end;
//als Stream versenden
MStr := TMemoryStream.Create;
  try
  MStr.Write(Rec,SizeOf(Rec));
  MStr.Position := 0;
  Form5.ClientSocket1.Socket.SendStream(MStr);
  finally
  if Assigned(MStr) then
    begin
    MStr := nil;
    MStr.Free;
    end;
  end;
end;
Und hier noch die Empfangsprozedur am Server:
Delphi-Quellcode:
procedure TServerForm.ServerSocket1ClientRead(Sender: TObject;Socket: TCustomWinSocket);
var ....
    iLen: integer;
    Rec : CustomRec;
    Bfr : Pointer;
begin
Finalize(Rec);
Rec.RecListe := TStringList.Create; //Stringliste des Records erzeugen
//Länge des Streams empfangen
iLen := Socket.ReceiveLength;
//Arbeitsspeicher reservieren
GetMem(Bfr, iLen);
//Stream empfangen und in Arbeitsspeicher schreiben
   try
   Socket.ReceiveBuf(Bfr^, iLen);
   //Position auf das Ende des Streams setzen
   MStream.Seek(0,soFromEnd);
   MStream.Write(Bfr^, iLen);
   finally
   FreeMem(Bfr);
   end;
MStream.Seek(0,soFromEnd);
//an erste Position gehen
MStream.Position := 0;
//Buffer in Record einlesen
MStream.ReadBuffer(Rec,iLen);

//wenn ich jetzt auf die Stringliste des Records zugreifen will, gibts eine Exception
//die Anzeige der Strings oder Integerwerte des Records funktioniert dagegen problemlos
//lasse ich Server und Client testweise auf ein und dem selben Rechner laufen, funktioniert es wunderbar
showmessage(Rec.RecTNListe.Strings[0]);
...
end;
Es scheint so, dass ich nicht wirklich die Stringliste per Sendstream übertrage, obwohl diese am Client definitv in den Record geschrieben wird, sondern nur Pointer auf die Adressen der Daten der Stringliste. Da der (entfernte) Server diese Adressen des Clients nicht kennt, kommt die Exception.

Die Frage ist also, wie bekomme ich den INHALT der Stringliste übertragen und auch wieder ausgelesen?

Danke schon mal für Eure Mithilfe.

mjustin 10. Feb 2017 23:38

AW: Stringlist mit SendStream versenden
 
Was nicht passt, wird passend gemacht: um eine Datenstruktur über Sockets (Streams) zu senden und zu empfangen, wird sie in ihre Teile zerlegt und jeweils aus diesen Teilen wieder zusammengesetzt. Wenn Sender und Empfänger den Strukturaufbau genau kennen, braucht der Empfänger nur die eintreffenden Daten in der richtigen Reihenfolge einzulesen.

Sehr einfach geht das mit den Read- und Write Funktionen aus Indy, die im IdTCPClient.IOHandler zur Verfügung stehen. Dinge mit flexibler Länge wie Strings oder Byte Arrays senden dabei zuerst die Länge und dann den Inhalt. So weiss der IOHandler des Empfängers, wie viele Bytes er lesen muss.

Diese Zerlegung auf primitive Objekte, die einzeln übertragen werden, ist sehr low-level und oft ausreichend. Für flexiblere Strukturen sind XML oder JSON als Zwischenformat geeigneter, da Sender und Empfänger alles als einen einzigen String senden und empfangen. Lediglich ein XML oder JSON Parser wird dazu noch benötigt.

himitsu 11. Feb 2017 00:13

AW: Stringlist mit SendStream versenden
 
Du versendest nur den Zeiger auf das Objekt und wunderst dich dann zurecht, dass am Ziel der Zeiger nur auf Schrott zeigt?

String[15] ist ein ShortString, also ein Record und liegt somit komplett innerhalb des Speichers deines eigenen Records.
Hättest du String verwendet, wäre dir dort das Selbe passiert.

Du darfst also nicht den Zeiger versenden, sondern mußt den Text-Inhalt manuell einzeln versenden und drüben in eine neue TStringList kopieren.

runningsoft 11. Feb 2017 08:56

AW: Stringlist mit SendStream versenden
 
Genau das ist ja mein Problem. Wie bekomme ich den Inhalt der Stringlist versendet und nicht nur den Zeiger darauf.

Ich hatte es zwischenzeitlich über TByteArray versucht. Ich habe die Stringlist also Byte für Byte in das Bytearray eingelesen und versendet. Hat auch funktioniert, hat allerdings den Nachteil, dass das Bytearray ein statisches Array ist und eine feste Größe von 32767 Byte hat, die Stringlistes aber nicht annähernd diese Größe erreicht. Ist also nicht so ganz befriedigend.

Anschließend hab ich es mit einem dynamischen Array aus TBYTE versucht. Da das aber ein dynamisches Array ist, stehe ich vor dem gleichen Problem wie mit der Stringlist, es werden nur die Zeiger versendet, nicht aber der Inhalt.

Für einen Tipp und ein paar Codeschnipsel, wie ich den Inhalt der Stringlist auf der Clientseite aufbereiten muss, um ihn zu versenden und auf der Serverseite wieder in eine Stringliste hineinbekomme, wäre ich dankbar.

dummzeuch 11. Feb 2017 09:28

AW: Stringlist mit SendStream versenden
 
Zitat:

Zitat von runningsoft (Beitrag 1361313)
Genau das ist ja mein Problem. Wie bekomme ich den Inhalt der Stringlist versendet und nicht nur den Zeiger darauf.

Ich wuerde stringlist.Text versenden. Beim Emfpaenger dann im neuen Record eine Stringlist anlegen (der dort gespeicherte Pointer ist ja ungueltig, also einfach eine neue erzeugen und zuweisen) und dieser den empfangenen Text zuweisen.

samso 11. Feb 2017 15:42

AW: Stringlist mit SendStream versenden
 
Du kannst den Record nicht komplett senden, sondern musst den String gesondert behandeln. Die Stringliste ist dann nicht mehr Bestandteil des Records, sondern wird mit einem zweiten Schreibbefehl zu dem MemoryStream hinzugefügt. Auf der Empfangsseite muss das Ganze wieder entsprechend zerlegt werden.
Delphi-Quellcode:
 
type
CustomRec = record
   RecID : Integer;
   RecVorgang : String[15];
   RecStatus: integer;
   ....
   RecStrLen: Integer; //<- damit weiß ich auf der Empfangsseite wie lang der der nachfolgende Text ist
  end;
...
  temp := Stringlist.Text;
  RecStrLen := Length(temp);
  MStr.Write(Rec,SizeOf(Rec));
  MStr.Write(Pointer(temp)^, Length(temp));
  MStr.Position := 0;


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:31 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