Autor Beitrag
LittleBen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 258
Erhaltene Danke: 4

Win 7, Mac OS
Delphi 7
BeitragVerfasst: Di 24.12.13 14:58 
Halli hallo und frohe Weihnachten!
Während dem Warten auf das Christkind habe ich mich mal mit dem Versenden eines Records, welches ein dynamisches Array beinhaltet, auseinanderngesetzt.

Das Record sieht so aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
type
  TEvent = record
   Name: string[255];
   {...}
  end;

  TDay = record
   Events: array of TEvent;
  end;

  TWeek = record
   Days: array[0..4of TDay;
  end;


Der Server reagiert folgendermaßen auf eine Anfrage des Clients:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
procedure TServerEventHandler.OnServerExecute(AThread: TIdPeerThread);
var cCmd, cIP: String;
    Week: TWeek;
    Len: LongInt;
    Stream: TStream;
begin
 try
  cCmd:= Trim(AThread.Connection.ReadLn);
  cIP:= AThread.Connection.Socket.Binding.IP;

  WriteLn('['+DateTimeToStr(now)+', '+cIP+']'+' '+cCmd);

  SetLength(Week.Days[0].Events,High(Week.Days[0].Events)+2);
  Week.Days[0].Events[High(Week.Days[0].Events)].Name:= 'Weihnachten';

  Stream:= TMemoryStream.Create;
  try
   Len:=SizeOf(TWeek);
   Stream.Write(Len, SizeOf(Len));
   Stream.Write(Week, Len);
   Stream.Seek(0, soFromBeginning);
   AThread.Connection.WriteStream(Stream,true,true,len);
  finally
   Stream.Free;
  end;

 finally
  AThread.Connection.Disconnect;
 end;
end;


Der Client holt sich die Daten so:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
var Week: TWeek;
    Stream: TStream;
    Len: LongInt;
    n1,n2: integer;
begin
 Stream:=TMemoryStream.Create;
 try
  IdTCPClient1.WriteLn('Gebe mir die aktuelle Woche');
  IdTCPClient1.ReadStream(Stream);

  Stream.Position:=0;
  Stream.Read(Len, SizeOf(Len));
  Stream.Read(Week, Len);

  for n1:= 0 to High(Week.Days) do
   for n2:= 0 to High(Week.Days[n1].Events) do
     Memo1.Lines.Add(Week.Days[n1].Events[n2].Name);
 finally
  Stream.Free;
 end;


Beim empfangen der Antwort gibt es eine Zugriffsverletzung.
Kann mir jemand erklären, wie ich vorgehen muss?

Viele Grüße und eine schöne Weihnachtszeit
Mr_Emre_D
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 114
Erhaltene Danke: 14



BeitragVerfasst: Di 24.12.13 17:42 
Edit: --


Zuletzt bearbeitet von Mr_Emre_D am Sa 25.11.17 15:36, insgesamt 1-mal bearbeitet
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 24.12.13 23:22 
Und dann kann man auch gleich einfach Klassen verwenden mit eigenen LoadFromStream und SaveToStream Methoden und ist die Records gleich los. ;-)
LittleBen Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 258
Erhaltene Danke: 4

Win 7, Mac OS
Delphi 7
BeitragVerfasst: Mi 25.12.13 00:53 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Und dann kann man auch gleich einfach Klassen verwenden mit eigenen LoadFromStream und SaveToStream Methoden und ist die Records gleich los. ;-)
Da ich immer noch mit Delphi 7 arbeite, bleibt mir auch nicht anderes übrig, als mit Klassen zuarbeiten ;)

Habs einfach mal so versucht:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
type
  TEvent = class
  private
   Name: string[255];
  public
   procedure read(Stream: TStream);
   procedure write(Stream: TStream);
  end;

  TDay = class
  private
   Events: array of TEvent;
  public
   procedure read(Stream: TStream);
   procedure write(Stream: TStream);
  end;

  TWeek = class
  private
   Days: array[0..4of TDay;
  public
   procedure read(Stream: TStream);
   procedure write(Stream: TStream);
  end;

{ TEvent }

procedure TEvent.write(Stream: TStream);
var
  len: Integer;
begin
  len := Length(Name);
  Stream.Write(len, SizeOf(len));
  if len > 0 then
    Stream.Write(Name[1], len * SizeOf(Name[1]));
end;

procedure TEvent.read(Stream: TStream);
var
  len: Integer;
begin
  Stream.Read(len, SizeOf(len));
  if len > 0 then
  begin
    SetLength(Name, len);
    Stream.Read(Name[1], len * SizeOf(Name[1]));
  end;
end;

{ TDay }

procedure TDay.read(Stream: TStream);
var
  i, len: Integer;
begin
  Stream.Read(len, SizeOf(len));
  SetLength(Events, len);
  for i := 0 to len - 1 do
    Events[i].read(Stream);
end;

procedure TDay.write(Stream: TStream);
var
  i, len: Integer;
begin
  len := Length(Events);
  Stream.Write(len, SizeOf(len));
  for i := 0 to len - 1 do
    Events[i].write(Stream);
end;

{ TWeek }

procedure TWeek.read(Stream: TStream);
var
  i: Integer;
begin
  for i := 0 to High(Days) do
    Days[i].read(Stream);
end;

procedure TWeek.write(Stream: TStream);
var
  i: Integer;
begin
  for i := 0 to High(Days) do
    Days[i].write(Stream);
end;

Server:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
procedure TServerEventHandler.OnServerExecute(AThread: TIdPeerThread);
var m: TMemoryStream;
    Sender: TWeek;
begin
  m:= TMemoryStream.Create;
  try
   Sender:= TWeek.Create;
   SetLength(Sender.Days[0].Events,1);
   Sender.Days[0].Events[0].Name := 'Testevent!';
   Sender.write(m);

   m.Seek(0, soFromBeginning);
   AThread.Connection.WriteStream(m);

   AThread.Connection.Disconnect;
  finally
   m.Free;
  end;
end;

Client:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
var m: TMemoryStream;
    Empfaenger: TWeek;
begin
 IdTCPClient1.Host:= '127.0.0.1';
 IdTCPClient1.Port:= 40000;
 IdTCPClient1.Connect;

 IdTCPClient1.WriteLn('Gib mir die Daten!');

 m:= TMemoryStream.Create;
 try
  Empfaenger:= TWeek.Create;
  IdTCPClient1.ReadStream(m);
  Empfaenger.read(m);
  ShowMessage(Empfaenger.Days[0].Events[0].Name);
 finally
   m.Free;
 end;


Nur tut sich da nichts. Es kommt nur die Meldung: "Verbindung wurde erfolgreich geschlossen!"
Mr_Emre_D
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 114
Erhaltene Danke: 14



BeitragVerfasst: Mi 25.12.13 02:01 
Edit: --


Zuletzt bearbeitet von Mr_Emre_D am Sa 25.11.17 15:36, insgesamt 1-mal bearbeitet
LittleBen Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 258
Erhaltene Danke: 4

Win 7, Mac OS
Delphi 7
BeitragVerfasst: Mi 25.12.13 02:19 
Diese Fehlermeldung bekam ich voher schonmal. Das hat eher was mit dem Stream zutun, die falsch gesendet werden, vermut ich mal. Das Read hab ich in das OnConnected-Event reingepackt -> kein Unterschied :(
Quitzlinga
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 60
Erhaltene Danke: 2

Win XP
Delphi 2007 Prof. Codegear Win32
BeitragVerfasst: Mi 25.12.13 12:30 
Hi,

Zitat:
AThread.Connection.WriteStream(m);
AThread.Connection.Disconnect;


Zum Zeitpunkt des Disconnects ist noch nicht gesagt, das du auch alle Streamdaten bekommen hast. Bevor Du beendest, solltes Du den Sendepuffer des Servers vorher leeren (Flush).
Was Dir generell noch fehlt ist ein Protokoll, das die Sende und Empfangsproblematik regelt.

MfG

Quitzlinga
LittleBen Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 258
Erhaltene Danke: 4

Win 7, Mac OS
Delphi 7
BeitragVerfasst: Mi 25.12.13 18:40 
Ich hab das mal eben als Testprojekt in den Anhang gepackt.
Auch mit dem Flush funktioniert es nicht. Vorher hat die Übertragung ja auch so funktioniert :?:
Einloggen, um Attachments anzusehen!
Quitzlinga
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 60
Erhaltene Danke: 2

Win XP
Delphi 2007 Prof. Codegear Win32
BeitragVerfasst: Mi 25.12.13 21:08 
Hi,

Zitat:
Sender.Days[0].Events[0].Name := 'Testevent!';


An dieser Stelle knallts im Server und er schliesst die Verbindung automatisch. Warum ? Sender wird zwar erzeugt, aber nicht Days und auch nicht Events.

MfG

Quitzlinga
LittleBen Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 258
Erhaltene Danke: 4

Win 7, Mac OS
Delphi 7
BeitragVerfasst: Mi 25.12.13 21:47 
Tatsächlich ... :autsch:

Aber trotz folgendem funktioniert es nicht:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
var Stream: TMemoryStream;
    Sender: TWeek;
    i: integer;
begin
 Memo1.Lines.Add('['+DateTimeToStr(now)+', '+AThread.Connection.Socket.Binding.IP+']'+' '+Trim(AThread.Connection.ReadLn));
 Stream:= TMemoryStream.Create;
 try
  Sender:= TWeek.Create;
  for i := 0 to High(Sender.Days) do
   Sender.Days[i]:= TDay.Create;

  SetLength(Sender.Days[0].Events,1);
  Sender.Days[0].Events[0]:= TEvent.Create;
  Sender.Days[0].Events[0].Name := 'Testevent!';
  Sender.Write(Stream);

  Stream.Seek(0, soFromBeginning);
  AThread.Connection.WriteStream(Stream);
  AThread.Connection.FlushWriteBuffer();
  AThread.Connection.Disconnect;
 finally
  Stream.Free;
 end;

Wenn ich
ausblenden Delphi-Quelltext
1:
2:
  AThread.Connection.FlushWriteBuffer();
  AThread.Connection.Disconnect;
raushaue, dann hängt sich der Client einfach auf, sonst passiert nichts. Mit den zwei Zeilen kommt wieder die Meldung "Verbindung wurde erfolgreich geschlossen!"
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 26.12.13 13:02 
Es ist sinnvoll, dass der Server dem Client zuerst mitteilt wie groß der Stream ist, der kommt. Stichwort: Protokoll... das fehlt dir noch komplett.

Wie man im Debugger sofort sieht hängt der Client beim Auslesen und bekommt den Disconnect nicht mit. Daher ist es besser gleich zu sagen wie viel Byte kommen werden und auch nur die zu lesen.

Dann benutzt du noch eine alte Indyversion. Das solltest du dringend ändern, du machst dir nur unnötig Arbeit und musst bei einem späteren Upgrade nur unnötig viel anpassen, wenn du noch Code für die alte Version schreibst. Eine aktuelle Version bekommst du z.B. hier:
indy.fulgan.com/ZIP/

Dann hast du den Code für deine Wochendefinition in Client und Server dupliziert. Das macht absolut keinen Sinn. Das gehört in eine gemeinsame Unit.

Im Anhang liegt eine bereinigte und funktionierende Version, getestet mit Delphi 7 Personal und der aktuellen Indy 10_5078 (nur im Bibliothekspfad eingetragen, nicht extra installiert).
Einloggen, um Attachments anzusehen!
LittleBen Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 258
Erhaltene Danke: 4

Win 7, Mac OS
Delphi 7
BeitragVerfasst: Do 26.12.13 14:41 
Der Jänicke mal wieder... viele vielen Dank!!! :flehan:
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 26.12.13 15:07 
Da siehst du dann auch gleich wie man generische Listen in deinem Delphi 7 so einbindet, dass sie auch später ab Delphi 2009 automatisch mit echten Generics benutzt werden. ;-)
LittleBen Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 258
Erhaltene Danke: 4

Win 7, Mac OS
Delphi 7
BeitragVerfasst: Do 26.12.13 15:58 
Ja das hat mich tief beeindruckt ^^ Werde versuche, das für alle folgenden Units von dem Typ bei zu behalten.
Aber davor hab ich doch noch eine Frage... Wenn die Event-Klasse ein weiteres String Attribut beinhaltet, wie muss ich das dann mit dem Stream handhaben?
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
type
  TEvent = class
  private
    FName: String;
    FLocation: String;
  public
    constructor Create(const ALoadFromStream: TStream); overload;
    constructor Create(const AName, ALocation: String); overload;
    procedure LoadFromStream(const AStream: TStream);
    procedure SaveToStream(const AStream: TStream);
    property Name: String read FName write FName;
    property Location: String read FLocation write FLocation;
  end;

Folgendes kann ja nicht stimmen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure TEvent.LoadFromStream(const AStream: TStream);
begin
 FName:= LoadStringFromStream(AStream);
 FLocation:= LoadStringFromStream(AStream);
end;

procedure TEvent.SaveToStream(const AStream: TStream);
begin
 SaveStringToStream(AStream, FName);
 SaveStringToStream(AStream, FLocation); 
end;
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Do 26.12.13 16:05 
Doch, das ist schon richtig. Du speicherst es hintereinander in den Stream und liest es entsprechend in der gleichen Reihenfolge wieder aus.
LittleBen Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 258
Erhaltene Danke: 4

Win 7, Mac OS
Delphi 7
BeitragVerfasst: Do 26.12.13 16:13 
Mein Delphi 7 hat sich mal wieder eine andere, gleichname Unit geholt... bin ja selber schuld :autsch: