Autor |
Beitrag |
LittleBen
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: 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:
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..4] of TDay; end; |
Der Server reagiert folgendermaßen auf eine Anfrage des Clients:
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:
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
Beiträge: 114
Erhaltene Danke: 14
|
Verfasst: Di 24.12.13 17:42
Zuletzt bearbeitet von Mr_Emre_D am Sa 25.11.17 15:36, insgesamt 1-mal bearbeitet
|
|
jaenicke
Beiträge: 19284
Erhaltene Danke: 1742
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: Mi 25.12.13 00:53
jaenicke hat folgendes geschrieben : | 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:
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..4] of TDay; public procedure read(Stream: TStream); procedure write(Stream: TStream); end;
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;
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;
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:
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:
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
Beiträge: 114
Erhaltene Danke: 14
|
Verfasst: Mi 25.12.13 02:01
Zuletzt bearbeitet von Mr_Emre_D am Sa 25.11.17 15:36, insgesamt 1-mal bearbeitet
|
|
LittleBen
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: 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
Beiträge: 60
Erhaltene Danke: 2
Win XP
Delphi 2007 Prof. Codegear Win32
|
Verfasst: 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
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: 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
Beiträge: 60
Erhaltene Danke: 2
Win XP
Delphi 2007 Prof. Codegear Win32
|
Verfasst: 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
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: Mi 25.12.13 21:47
Tatsächlich ...
Aber trotz folgendem funktioniert es nicht:
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 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
Beiträge: 19284
Erhaltene Danke: 1742
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: Do 26.12.13 14:41
Der Jänicke mal wieder... viele vielen Dank!!!
|
|
jaenicke
Beiträge: 19284
Erhaltene Danke: 1742
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: 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?
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:
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
Beiträge: 19284
Erhaltene Danke: 1742
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
Beiträge: 258
Erhaltene Danke: 4
Win 7, Mac OS
Delphi 7
|
Verfasst: Do 26.12.13 16:13
Mein Delphi 7 hat sich mal wieder eine andere, gleichname Unit geholt... bin ja selber schuld
|
|