Entwickler-Ecke
Internet / Netzwerk - Indy - Record mit dynamischen Array senden
LittleBen - Di 24.12.13 14:58
Titel: Indy - Record mit dynamischen Array senden
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 - Di 24.12.13 17:42
Edit: --
jaenicke - 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 - 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:
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..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 - Mi 25.12.13 02:01
Edit: --
LittleBen - 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 - 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 - 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 :?:
Quitzlinga - 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 - Mi 25.12.13 21:47
Tatsächlich ... :autsch:
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 - 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:
http://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).
LittleBen - Do 26.12.13 14:41
Der Jänicke mal wieder... viele vielen Dank!!! :flehan:
jaenicke - 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 - 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 - 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 - Do 26.12.13 16:13
Mein Delphi 7 hat sich mal wieder eine andere, gleichname Unit geholt... bin ja selber schuld :autsch:
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!