Autor Beitrag
::micha::
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Fr 15.02.08 14:02 
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


Zuletzt bearbeitet von ::micha:: am Mo 18.02.08 14:12, insgesamt 1-mal bearbeitet
opfer.der.genauigkeit
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 754
Erhaltene Danke: 1



BeitragVerfasst: Fr 15.02.08 16:21 
Ich glaube 'n bissl Code wäre hilfreich.

_________________
Stellen Sie sich bitte Zirkusmusik vor.
::micha:: Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Mo 18.02.08 09:09 
ausblenden 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


Zuletzt bearbeitet von ::micha:: am Mo 18.02.08 09:25, insgesamt 1-mal bearbeitet
elundril
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3747
Erhaltene Danke: 123

Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
BeitragVerfasst: Mo 18.02.08 09:14 
verwende bitte die [code]- Tags.

also so:

[delphi]

{<--//Hier dein Code//-->}

[/delphi]

danke,

lg elundril

_________________
This Signature-Space is intentionally left blank.
Bei Beschwerden, bitte den Beschwerdebutton (gekennzeichnet mit PN) verwenden.
opfer.der.genauigkeit
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 754
Erhaltene Danke: 1



BeitragVerfasst: Mo 18.02.08 14:30 
Schonmal den Port gecheckt?
Vergessen den Server auf Active zu setzen?

Ich habe das mal versucht nachzustellen.

Client:
ausblenden 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:
ausblenden 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.

_________________
Stellen Sie sich bitte Zirkusmusik vor.
::micha:: Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Mo 18.02.08 15: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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 754
Erhaltene Danke: 1



BeitragVerfasst: Mo 18.02.08 16:52 
Wie sendest du deine Daten?

_________________
Stellen Sie sich bitte Zirkusmusik vor.
Xentar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2077
Erhaltene Danke: 2

Win XP
Delphi 5 Ent., Delphi 2007 Prof
BeitragVerfasst: Mo 18.02.08 17: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:: Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Di 19.02.08 13:09 
ich sende die Daten mit "WriteLn('')"
opfer.der.genauigkeit
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 754
Erhaltene Danke: 1



BeitragVerfasst: Di 19.02.08 13:14 
WriteLn? Hm... das erinnert mich an einen Code, den ich mal irgendwo hier im Forum gepostet habe...
ich glaube der verwendet ReadLn um die Daten zu empfangen...
:P

_________________
Stellen Sie sich bitte Zirkusmusik vor.
::micha:: Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Di 19.02.08 14:43 
was willst du mir damit sagen??
opfer.der.genauigkeit
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 754
Erhaltene Danke: 1



BeitragVerfasst: Di 19.02.08 17: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.

_________________
Stellen Sie sich bitte Zirkusmusik vor.
::micha:: Threadstarter
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Mi 20.02.08 08:42 
wie schon im ersten posting geschrieben liefert ReadLn leider gar keine Ergebnis, mir wäre diese Funktion sicher die Liebste ;)
opfer.der.genauigkeit
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 754
Erhaltene Danke: 1



BeitragVerfasst: Mi 20.02.08 14: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.

_________________
Stellen Sie sich bitte Zirkusmusik vor.
Quitzlinga
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 60
Erhaltene Danke: 2

Win XP
Delphi 2007 Prof. Codegear Win32
BeitragVerfasst: Fr 22.02.08 10: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 user profile iconopfer.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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 39



BeitragVerfasst: Sa 23.02.08 11: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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 754
Erhaltene Danke: 1



BeitragVerfasst: So 24.02.08 13: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...
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:
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...
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:
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)

_________________
Stellen Sie sich bitte Zirkusmusik vor.


Zuletzt bearbeitet von opfer.der.genauigkeit am So 24.02.08 16:49, insgesamt 2-mal bearbeitet
artelogic
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 39



BeitragVerfasst: So 24.02.08 14:37 
ausblenden 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 user profile iconNarses: Quote- durch Delphi-Tags ersetzt
opfer.der.genauigkeit
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 754
Erhaltene Danke: 1



BeitragVerfasst: So 24.02.08 16:08 
user profile iconopfer.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.

_________________
Stellen Sie sich bitte Zirkusmusik vor.
artelogic
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 39



BeitragVerfasst: So 24.02.08 18: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.