Entwickler-Ecke
Internet / Netzwerk - TCP Indy Read
::micha:: - Fr 15.02.08 15:02
Titel: TCP Indy Read
Hallo,
ich habe ein Problem beim Lesen der Daten.
Das die Daten ordentlich gesendet und empfangen werden überprüfe ich mit einem Netzwerkmonitor.
Beim Lesen in der Methode IdTCPServerExecute kommt es allerdings für mich zu unverständlichen Dingen.
1. Versuch: AContext.Connection.IOHandler.ReadLn
bringt leider gar keinen String
2. Versuch: AContext.Connection.IOHandler.ReadString(9)
bring mir die daten, leider sind aber die Bytes teilweise in der Reihenfolge vertauscht
außerdem bringt es mir auch keine daten mehr, wenn ich zum Beipiel 565 Bytes auslesen will.
und wie gesagt, dass obwohl die Daten richtig im Netz umherschwirren
ich nutze Indy 10.1.5
ich hoffe ihr könnt mir weiterhelfen
gruß
Micha
opfer.der.genauigkeit - Fr 15.02.08 17:21
Ich glaube 'n bissl Code wäre hilfreich.
::micha:: - Mo 18.02.08 10:09
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| procedure TForm1.IdTCPServerExecute(AContext: TIdContext); begin If Not AContext.Connection.IOHandler.Readable Then Exit; if bVersionLesen then begin self.Memo.Lines.Add(AContext.Connection.IOHandler.ReadString(9)); end; if bStatusabfrage then begin self.Memo.Lines.Add(AContext.Connection.IOHandler.ReadString(40)); end; if bWerkzeugdatenLesen then begin self.Memo.Lines.Add(AContext.Connection.IOHandler.ReadString(565)); end; end; |
hier nun wie gewünscht etwas code
gruß
Micha
elundril - Mo 18.02.08 10:14
verwende bitte die [code]- Tags.
also so:
[delphi]
{<--//Hier dein Code//-->}
[/delphi]
danke,
lg elundril
opfer.der.genauigkeit - Mo 18.02.08 15:30
Schonmal den Port gecheckt?
Vergessen den Server auf Active zu setzen?
Ich habe das mal versucht nachzustellen.
Client:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| procedure TForm1.FormCreate(Sender: TObject); begin IdTCPClient1.Host := '127.0.0.1'; IdTCPClient1.Port := 666; end;
procedure TForm1.Button1Click(Sender: TObject); begin IdTCPClient1.Connect; IdTCPClient1.Socket.WriteLn( Memo1.Lines.Text ); IdTCPClient1.Disconnect; end; |
Server:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure TForm2.FormCreate(Sender: TObject); begin IdTCPServer1.DefaultPort := 666; IdTCPServer1.Active := true; end;
procedure TForm2.IdTCPServer1Execute(AContext: TIdContext); begin Memo1.Lines.Add( AContext.Connection.IOHandler.ReadLn ); end; |
Der Code soll bitte nur zeigen, dass es geht, wie es geht und
erhebt keinen Anspruch auf ernsthafte Qualität.
::micha:: - Mo 18.02.08 16:36
Port und Active ist ok!
es klappt ja auch beim lesen von nur 9 bytes aber bei 565 geht es nicht und bei Readln oder allData passiert nix.
wer kann mir helfen?
danke
Micha
opfer.der.genauigkeit - Mo 18.02.08 17:52
Wie sendest du deine Daten?
Xentar - Mo 18.02.08 18:28
Ich weiß nicht, ob das bei Indy10 immer noch so ist, aber bei Indy9 hatte ich häufiger Probleme mit der Funktion Readable, da diese teilweise ein falsches Ergebnis lieferte. Vielleicht hab ich auch nur die Funktion falsch verwendet.
Hab das immer so gemacht, dass ich ein Read mit Timeout aufgerufen hab, und wenn der String = '' war, wurde eben nichts empfangen.
::micha:: - Di 19.02.08 14:09
ich sende die Daten mit "WriteLn('')"
::micha:: - Di 19.02.08 15:43
was willst du mir damit sagen??
opfer.der.genauigkeit - Di 19.02.08 18:17
Wenn du eh über WriteLn schreibst, musst du dir nicht die Mühe machen und komplizierter lesen als nötig.
Bei ReadString musst du immer wissen, wieviele Daten im Eingang sind.
Wird ein zu Großer ReadCount angegeben, schlägt die Funktion fehl.
::micha:: - Mi 20.02.08 09:42
wie schon im ersten posting geschrieben liefert ReadLn leider gar keine Ergebnis, mir wäre diese Funktion sicher die Liebste ;)
opfer.der.genauigkeit - Mi 20.02.08 15:53
Auch nicht bei dem Code, den ich dir gepostet habe?
Ich habe das bei mir, lokal allerdings, getestet und es bestens funktioniert.
Sofern dein Code nicht Topsecret ist könntest du ihn ja mal komplett posten oder als Zip anhängen.
Quitzlinga - Fr 22.02.08 11:51
Wie stellst Du die Länge fest, die Du liest ? Wenn es Du es z.B. so gemacht hast, das Du einen String mittels WriteLn sendest (z.B. WriteLn('12345678'); = 8 Zeichen) und dann einfach versuchst am Client eine bestimmte Anzahl Zeichen zu lesen (Das tut deine Funktion nämlich), so wird es vermutlich nicht klappen. Grund : WriteLn sendet zusätzlich zu Deinen Zeichen noch ein CRLF (Carriage Return Line Feed = 2 Zeichen), das sogenannte Terminatorzeichen bei INDY. So kann die Funktion ReadLn erkennen, wann ein kompletter Datenstring vorliegt und würde sonst solange warten, bis ein CRLF auftaucht. Ich vermute mal, das Du diese zusätzlichen Zeichen nicht berücksichtigst hast.
Aber eine weitere Frage : Wie stellst Du überhaupt fest, wieviel Zeichen du sendest ? Deine Server Execute Funktion hat zwar Verzweigungen aber wo stellst Du bVersion, bStatus etc ein ? Diese Information musst Du deinem Server ja auch irgentwie zur Verfügung stellen, sonst wird er ohne das obige Problem zu berücksichtigen in 1/3 der Fälle ohnehin die falsche Anzahl an Informationen lesen.
Ich rate Dir zu der Lösung von
opfer.der.genauigkeit. Ohnehin wirst Du später sonst Probleme bekommen, wenn Du Deinen Code erweiterst, da Du ständig darauf achten musst, die richtige Anzahl an Informationen zu lesen.
[/user]
MfG
Quitzlinga
artelogic - Sa 23.02.08 12:13
Ich schätze mal, du beachtest nicht, daß TIdTCPServer multithreaded ist. Dh, das Execute-Event läuft nicht im MainThread und sollte somit nicht auf VCL-Objekte (die nämlich nicht thread-safe sind) zugreifen, weil es sonst zu unvorhersehbaren und teilweise nicht reproduzierbaren Effekten kommt, wie du ja merkst. ;)
opfer.der.genauigkeit - So 24.02.08 14:29
Nachdem ich das Thema des Oefteren antreffe, habe ich mir einen kurzen Einblick in die Indykomponenten gegönnt.
Meine Lösung ist mit Sicherheit nicht perfekt und einen Timer sollte man für soetwas auch garnicht verwenden :mrgreen:, aber ich sehe das als Denkhilfe für Anfänger und da muss man nicht noch mit Threads antanzen. ;)
Man erstelle ein Formular mit einer TIdTCPServer-Komponente und einem TMemo.
Man kopiere folgenden Code...
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:
| unit Unit2;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPServer;
type TForm2 = class(TForm) Memo1: TMemo; IdTCPServer1: TIdTCPServer; procedure IdTCPServer1Execute(AContext: TIdContext); procedure IdTCPServer1Connect(AContext: TIdContext); procedure IdTCPServer1Disconnect(AContext: TIdContext); procedure FormCreate(Sender: TObject); end;
var Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.FormCreate(Sender: TObject); begin IdTCPServer1.DefaultPort := 666; IdTCPServer1.Active := true; end;
procedure TForm2.IdTCPServer1Connect(AContext: TIdContext); begin AContext.Connection.IOHandler.WriteLn( 'Hallo' ); Memo1.Lines.Add( 'Client connected' ); end;
procedure TForm2.IdTCPServer1Disconnect(AContext: TIdContext); begin Memo1.Lines.Add( 'Client disconnected' ); end;
procedure TForm2.IdTCPServer1Execute(AContext: TIdContext); var s: string; begin s := AContext.Connection.IOHandler.ReadLn;
if not ( s = '' ) then begin Memo1.Lines.Add( s ); AContext.Connection.IOHandler.WriteLn( 'Got message.' ); end; end;
end. |
... Server fertig.
Zum Client... Forumlar erstellen, TIdTCPClient-Komponente + TMemo + TButton + TTimer...
Der Code...
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:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, ExtCtrls;
type TForm1 = class(TForm) IdTCPClient1: TIdTCPClient; Memo1: TMemo; Button1: TButton; Timer1: TTimer; procedure Button1Click(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure FormCreate(Sender: TObject); end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject); begin IdTCPClient1.Host := '127.0.0.1'; IdTCPClient1.Port := 666; IdTCPClient1.Connect; end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin IdTCPClient1.Disconnect; end;
procedure TForm1.Timer1Timer(Sender: TObject); begin IdTCPClient1.IOHandler.CheckForDataOnSource( 5 );
if ( IdTCPClient1.IOHandler.InputBuffer.Size > 0 ) then begin Memo1.Lines.Add( IdTCPClient1.IOHandler.ReadLn ); end; end;
procedure TForm1.Button1Click(Sender: TObject); begin IdTCPClient1.IOHandler.WriteLn( Memo1.Text ); end;
end. |
So, fertig.
Noch ein paar Worte:
Für einen vernünftigen Chat reicht das nicht! Nein, auch einen TTimer sollte man an dieser Stelle nicht platzieren -> siehe Fiber oder Threads.
Wenn jemand weiß, ob es einen internen Thread gibt, der beim Empfang neuer Daten ein entsprechendes Event ausführt... immer her damit.
Des Weiteren sei angemerkt, dass das Execute vom Server sowohl beim Senden, also auch beim Empfangen von Daten aufgerufen wird. Das ist evtl. eine Randnotiz, die gerade Neulingen eine menge Verwirrung sparen kann.
Wünsche werden ignoriert, Kritik ist willkommen. :gruebel:
//Edit: Execute im Server ist nicht Threadsafe! (siehe ein Post weiter unten)
artelogic - So 24.02.08 15:37
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| procedure TForm2.IdTCPServer1Execute(AContext: TIdContext); var s: string; begin s := AContext.Connection.IOHandler.ReadLn;
if not ( s = '' ) then begin Memo1.Lines.Add( s ); AContext.Connection.IOHandler.WriteLn( 'Got message.' ); end; end; |
läuft nur weil man in aller regel im leben mehr glück als pech hat. ;)
hat aber nix mit sauberer programmierung zu tun. nix für ungut, aber den vcl-zugriff (hier Memo1.Lines.Add(s)) musst du synchronisieren, sonst knallt es, wenn dich das glück verläßt...
Moderiert von
Narses: Quote- durch Delphi-Tags ersetzt
opfer.der.genauigkeit - So 24.02.08 17:08
opfer.der.genauigkeit hat folgendes geschrieben: |
| Meine Lösung ist mit Sicherheit nicht perfekt und einen Timer sollte man für soetwas auch garnicht verwenden :mrgreen:, aber ich sehe das als Denkhilfe für Anfänger und da muss man nicht noch mit Threads antanzen. ;) |
Ich dachte damit hätte ich alles gesagt...
Allein einen Timer zu verwenden...
Ich würde nicht mal die Indy-Komponeten verwenden, wenn es ein ernsthaftes Projekt werden soll. ;)
Aber ich mache mal 'n Edit oben.
artelogic - So 24.02.08 19:07
sei mir nicht böse, aber ich versteh den denkansatz nicht. hier hat doch jemand ein konkretes problem gepostet, oder?
und ich schätze mal, der will sein problem gelöst wissen und nicht irgendein firlefanz gelaber hören. und sein problem ist, daß er aus einem anderen als dem MainThread auf VCL-Komponenten zugreift. und da kannste jetzt nicht einfach sagen, vergessen wir mal die threads und lösen das problem indem wir es ignorieren.
und ich weiß auch nicht, was gegen einen timer spricht, wenn es was zu timen gibt. und noch viel weniger weiß ich, was an den indy komponenten auszusetzen ist. die sind durchaus robust genug, um sie bei professionellen projekten einzusetzen.
opfer.der.genauigkeit - Mo 25.02.08 02:51
artelogic hat folgendes geschrieben: |
| sei mir nicht böse, aber ich versteh den denkansatz nicht. |
tut mir leid...
artelogic hat folgendes geschrieben: |
hier hat doch jemand ein konkretes problem gepostet, oder?
und ich schätze mal, der will sein problem gelöst wissen und nicht irgendein firlefanz gelaber hören.
|
Ja, mit IdTCPClient1.IOHandler.CheckForDataOnSource( x ) & IdTCPClient1.IOHandler.InputBuffer.Size weiß er auch wieviele Daten er bekommt.
artelogic hat folgendes geschrieben: |
und sein problem ist, daß er aus einem anderen als dem MainThread auf VCL-Komponenten zugreift. und da kannste jetzt nicht einfach sagen, vergessen wir mal die threads und lösen das problem indem wir es ignorieren.
|
Ja du hast Recht, wie konnte ich das wesentliche Problem nur uebersehen. :mrgreen:
Deswegen verweise ich gleich mal.
http://www.michael-puff.de/Developer/Delphi/Tutorials/Threads_mit_Delphi.pdf
artelogic hat folgendes geschrieben: |
und ich weiß auch nicht, was gegen einen timer spricht, wenn es was zu timen gibt. und noch viel weniger weiß ich, was an den indy komponenten auszusetzen ist. die sind durchaus robust genug, um sie bei professionellen projekten einzusetzen. |
Ja... debug sie mal und lies dir die TODO-Liste durch...
Jeder hat einen anderen Qualitaetsanspruch. ;)
... aber es ist auch Geschmackssache... ich wuerde sie niemals fuer kommerzielle Produkte einsetzen...
Das ist meine Meinung, die muss man nicht gut finden. :twisted:
opfer.der.genauigkeit - Mo 25.02.08 08:51
Achso, weil ich es nicht erwähnt habe.
Die Funktion ReadString(x) löst eine Exception aus, wenn zu viele Daten gelesen werden.
Das liegt - wenn ich mich recht entsinne - daran, dass beim Lesen festgestellt wird, dass der Socket schon geschloßen ist und der Server fröhlich weiter Daten liest, obwohl keine mehr da sind.
Ohne try...except fällt einem das nicht auf und der gelesene String bleibt leer.
::micha:: - Mo 25.02.08 11:15
Der Client benutzt die Indy Komponente garnicht, sondern ist wohl in C++ geschrieben und auf den habe ich auch keinen einfluss.
Ich weiß nur, was ich zu erwarten habe, wenn ich den ein oder anderen Befehl sende.
Gruß
Micha
opfer.der.genauigkeit - Mo 25.02.08 11:42
opfer.der.genauigkeit hat folgendes geschrieben: |
... IdTCPClient1.IOHandler.CheckForDataOnSource( x ) & IdTCPClient1.IOHandler.InputBuffer.Size ...
|
Was kommt bei dir denn dabei raus?
::micha:: - Mi 27.02.08 11:48
wie gesagt, die Clientkomponente ist nicht von mir und da kann ich auch nix dran rumprogrammieren.
ich lese jetzt die daten mit ReadBytes, das scheint zu funktionieren.
gruß
Micha
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 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!