INDY 10 | TCPServer , TCPClient

Rund um die LCL und andere Komponenten
Pascal95

INDY 10 | TCPServer , TCPClient

Beitrag von Pascal95 »

Hallo,

ich habe mir die Indy Komponente für Lazarus heruntergeladen.
Dazu habe ich diese hier benutzt (http://www.delphipraxis.net/149405-indy ... ost1008457).
So habe ich die indylaz.lpk kompiliert und installiert.
Allerdings wollte ich soetwas wie ein Nachrichten-Austausch zwischen Client und Server realisieren.
Dazu helfen mir die Delphi-Beispiele nicht weiter und im Paket waren keine angepassten Beispiele, bzw. solche für die man noch zusätzliche Packages installieren musste (indysystemlaz,...) .
Kann mir vielleicht jemand ein Beispiel-Code schreiben, falls es denn überhaupt gehen sollte.

So etwas habe ich bereits probiert:

Code: Alles auswählen

// Client
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  IdTCPClient1.Host:='127.0.0.1';
  IdTCPClient1.Port:=12345;
 
  try
    idTCPClient1.Connect;
    except showmessage('Keine Verbindung möglich');
   end;
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
  IdTCPClient1.IOHandler.WriteLn(Edit1.Text);
end;
und für den Server:

Code: Alles auswählen

// Server
 
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
  A: String;
begin
  A:=AContext.Connection.IOHandler.ReadLn;
  ShowMessage(A);
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  IdTCPServer1.DefaultPort:=12345;
  IdTCPServer1.Active:=True;
end;

Ohne Erfolg:
Bild

Der Server reagiert nicht mehr und muss über den Taskmanager beendet werden.


Vielen Dank für jede Unterstützung
Pascal

Pascal95

Re: INDY 10 | TCPServer , TCPClient

Beitrag von Pascal95 »

Hat keiner eine Idee?

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: INDY 10 | TCPServer , TCPClient

Beitrag von Scotty »

Der Fehler entsteht, wenn du in einem Thread Dinge machst, die da nicht hingehören - ShowMessage zum Beispiel. Kontrolliere nochmal, dass die Beispiele richtig umsetzt. Mit erscheint es nicht sinnvoll, was da in Execute() steht.

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1633
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: INDY 10 | TCPServer , TCPClient

Beitrag von corpsman »

Damit meint scotty ungefähr :

Code: Alles auswählen

Type IdTCPServer1 = Class
  private
  public
   tmps:String;
   Procedure Sync;
  end;
 
Procedure IdTCPServer1.Sync;
begin
showmessage(tmps);
end;
 
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
begin
//  A:=AContext.Connection.IOHandler.ReadLn;
  tmps :=AContext.Connection.IOHandler.ReadLn;
  synchronize(@sync);
//  ShowMessage(A);
end;
--
Just try it

Pascal95

Re: INDY 10 | TCPServer , TCPClient

Beitrag von Pascal95 »

Danke schonmal, aber die Prozedur (?) synchronize wird nicht gefunden.
Wo soll die sein und kann die globale Variable und die Prozedur nicht auch so gesetzt werden ?

Code: Alles auswählen

type
 
  { TForm1 }
 
  TForm1 = class(TForm)
    IdTCPServer1: TIdTCPServer;
    procedure FormCreate(Sender: TObject);
    procedure IdTCPServer1Execute(AContext: TIdContext);
    Procedure Sync;
  private
    { private declarations }
  public
    { public declarations }
  end;
 
 
var
  Form1: TForm1;
  tmps:String;
Vielleicht hat jemand noch eine Idee?

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1633
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: INDY 10 | TCPServer , TCPClient

Beitrag von corpsman »

Also :

was ich ( und ich glaube scotty auch) damit sagen wollte.

TIdTCPServer ist soweit ich weis ein eigener Tread und wenn du einen Thread hast, dann darfst du nicht einfach aus dem Thread heraus eine LCL Methode aufrufen. Sonst krachts

in der uses deines Projektes muss zudem noch die Unit

Code: Alles auswählen

uses
  cthreads,
eingebunden sein, zumindest ist das unter Linux so.

Synchronize ist bei mir definiert in classes.inc als Methode von TThread..
--
Just try it

Pascal95

Re: INDY 10 | TCPServer , TCPClient

Beitrag von Pascal95 »

Hallo,

also cthreads wird nicht gefunden, wenn ich es in die uses einbinde, es ist allerdings hier: C:\lazarus\fpc\2.2.4\source\rtl\unix\cthreads.pp
Wenn ich dann schreibe:

Code: Alles auswählen

uses
..., cthreads in 'C:\lazarus\fpc\2.2.4\source\rtl\unix\cthreads.pp';
könnte es ja auch funktionieren. Aber es scheint, dass man die Unit nur unter Linux braucht(?)

Synchronize finde ich in der classes.inc so vor:

Code: Alles auswählen

class procedure TThread.Synchronize(AThread: TThread; AMethod: TThreadMethod);
  var
    LocalSyncException: Exception;
  begin
    { do we really need a synchronized call? }
    if GetCurrentThreadID=MainThreadID then
      AMethod()
    else
      begin
        System.EnterCriticalSection(SynchronizeCritSect);
        SynchronizeException:=nil;
        SynchronizeMethod:=AMethod;
 
        { be careful, after this assignment Method could be already executed }
        DoSynchronizeMethod:=true;
 
        RtlEventSetEvent(SynchronizeTimeoutEvent);
 
        if assigned(WakeMainThread) then
          WakeMainThread(AThread);
 
        { wait infinitely }
        RtlEventWaitFor(ExecuteEvent);
        LocalSyncException:=SynchronizeException;
        System.LeaveCriticalSection(SynchronizeCritSect);
        if assigned(LocalSyncException) then
          raise LocalSyncException;
      end;
  end;
 
 
procedure TThread.Synchronize(AMethod: TThreadMethod);
  begin
    TThread.Synchronize(self,AMethod);
  end;
Wenn ich dann allerdings cthreads auf die o.g. Weise einbinde findet Lazarus die Units nicht, die wiederum von cthreads abhängig sind.

Vielleicht kann jemand einen kurzen Beispiels-Code zeigen, der den Umgang mit INDY, vor allem mit IdTCPServer und IdTCPClient verdeutlicht.

Pascal

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6863
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: INDY 10 | TCPServer , TCPClient

Beitrag von af0815 »

Hast du einen guten Grund warum du Indy benötigst, oder geht es dir um die Funktion selbst ?

Ev. könnte Synapse einfacher von der Handhabung her sein als Indy.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Pascal95

Re: INDY 10 | TCPServer , TCPClient

Beitrag von Pascal95 »

Hallo af0815,

zu erst habe ich mich nur für das Ergebnis interessiert, dann aber immer mehr um die Funktionsweise von Indy selber.

Mit Synapse habe ich bereits eher wenig gemacht, aber schon installiert.

Wenn mir dann jemand helfen kann mit Synapse so eine Server-Client Verbindung zu erstellen, wäre ich auch sehr dankbar.

Pascal

Scotty
Beiträge: 768
Registriert: Mo 4. Mai 2009, 13:24
OS, Lazarus, FPC: Arch Linux, Lazarus 1.3 r44426M FPC 2.6.4
CPU-Target: x86_64-linux-qt/gtk2
Kontaktdaten:

Re: INDY 10 | TCPServer , TCPClient

Beitrag von Scotty »

Leider kann man beim Networking nicht vermeiden, dass Dinge in Threads bearbeitet werden, weil ja nicht in einer Art repeat-until-Schleife auf Daten gewartet werden soll. Indy hat AFAIK zwei Modi: blocking und nonblocking. Davon hängt ab, was du wie und wann machen musst. Synchronize() ist eine Methode der Klassen TThread, die in einer ähnlichen Variante bei non-blocking integriert sein muss. Deswegen nochmal: folge den Beispielen und lies die Hilfen. Mein Rat ist allerdings, die Finger erstmal davon zu lassen.

http://wiki.freepascal.org/Indy_with_Lazarus" onclick="window.open(this.href);return false;
http://www.indyproject.org/Sockets/Docs ... es.DE.aspx" onclick="window.open(this.href);return false;

Benutzeravatar
theo
Beiträge: 10938
Registriert: Mo 11. Sep 2006, 19:01

Re: INDY 10 | TCPServer , TCPClient

Beitrag von theo »

Pascal95 hat geschrieben: Wenn mir dann jemand helfen kann mit Synapse so eine Server-Client Verbindung zu erstellen, wäre ich auch sehr dankbar.
Bei Synapse ist ein Echo Server als Beispiel dabei. In source/demo/echo

Pascal95

Re: INDY 10 | TCPServer , TCPClient

Beitrag von Pascal95 »

Danke.
Das Projekt war zwar im Delphi-Format, habe es nun konvertiert und dann neu kompiliert.
Die Unit enthält einen Button "Run TCP Echo Server", der bewirkt, dass sich die Firewall meldet und das Programm erst mal geblockt ist.

Nunja, was sollte diese Echo-Unit überhaupt machen können?

Pascal

Benutzeravatar
theo
Beiträge: 10938
Registriert: Mo 11. Sep 2006, 19:01

Re: INDY 10 | TCPServer , TCPClient

Beitrag von theo »

Pascal95 hat geschrieben: Die Unit enthält einen Button "Run TCP Echo Server", der bewirkt, dass sich die Firewall meldet und das Programm erst mal geblockt ist.
Das ist der Job der Sicherheitssoftware. Hat mit Synapse und Lazarus nichts zu tun.
Pascal95 hat geschrieben: Nunja, was sollte diese Echo-Unit überhaupt machen können?
Der Echo-Server soll einen String empfangen und wieder zurücksenden. Was halt ein einfacher Server so tut.

Kann sein, dass der echo Port schon belegt ist.
Du kanst testweise auch einen anderen nehmen. z.B. bind('127.0.0.1','8080');

Thomas B.
Beiträge: 90
Registriert: Fr 2. Nov 2007, 13:32
OS, Lazarus, FPC: Win (L 1.0 FPC 2.6.0)
CPU-Target: 32Bit
Wohnort: Ulm

Re: INDY 10 | TCPServer , TCPClient

Beitrag von Thomas B. »

Hallo,
also ich benutze seit 2 Jahren die Indy-Komponenten (aktuell 10.2) in einem größeren Projekt (ca. 10 Server und 10 Clients)
und bin sehr zufrieden damit. Allerdings habe ich die Indy-Komponenten nicht in die IDE hinein kompiliert, sondern nutze "manuell" die Units.

Beispielcode für den Server:

Code: Alles auswählen

uses
  ...
  IdTCPServer,
  IdContext,
  ...
 
type
  TTestServer = class
    private
      fServer : TIdTCPServer;
      fContext: TIdContext;
      fClientIP : String;
      procedure MyOnConnect(AContext: TIdContext);
      procedure MyOnDisconnect(AContext: TIdContext);
      procedure MyOnExecute(AContext: TIdContext);
      function StartServer(Port: Integer): Boolean;
      function StopServer: Boolean;
    public
      constructor Create(MyServerPort: Integer);
      destructor Destroy; override;
  end;
 
const 
      IdleWaitTime = 500;
      ServerReadTimeout = 5000;
 
var MyServer: TTestServer; // Achtung: Objekt noch separat erzeugen mit MyServer := TTestServer(dein-Server-Port); bzw. Freigeben MyServer.Free;
 
implementation
...
 
procedure TTestServer.MyOnConnect(AContext: TIdContext);
begin
  // falls sich sich nur ein Client verbindet:
  fContext := AContext;
  fClientIP := AContext.Connection.Socket.Binding.PeerIP;
  //  Der Client fClientIP ist nun verbunden
end;
 
procedure TTestServer.MyOnDisconnect(AContext: TIdContext);
begin
  fContext := nil; // Client fClientIP ist nicht mehr verbunden
end;
 
procedure TTestServer.MyOnExecute(AContext: TIdContext);
// wird ständig als separater Thread (einer pro Client) ausgeführt,
// sobald ein Client verbunden wird bis er disconnected
begin
  // falls irgendetwas automatisiert zu tun ist, ansonsten:
  Sleep(IdleWaitTime);
end;
 
function TTestServer.StartServer(Port: Integer): Boolean;
begin
  fServer.Active := false;
  fServer.Bindings.Clear;
  fServer.DefaultPort := Port;
  fServer.Bindings.DefaultPort := Port;
  fServer.Bindings.Add;
  fServer.Active := true;
  Result := fServer.Active;
end;
 
function TTestServer.StopServer: Boolean;
begin
  Try
    If Assigned(fContext) then
      fContext.Yarn.Free;// dann kann auch bei verbundenen Client beendet werden
    fServer.Active := false;
    fServer.Bindings.Clear;
    Result := not fServer.Active;
  Except
    Result := false;
  end;
end;
 
constructor TTestServer.Create(MyServerPort: Integer);
begin
  inherited Create;
  fContext := nil; // kein Client verbunden
  fServer := TIdTCPServer.Create;
  fServer.OnConnect := @MyOnConnect;
  fServer.OnDisconnect := @MyOnDisconnect;
  fServer.OnExecute := @MyOnExecute;
 
  // Server automatisch starten
  If StartServer(MyServerPort) then begin
    // ...
  end;
end;
 
destructor TTestServer.Destroy;
begin
  StopServer;
  fServer.Free;
  inherited Destroy;
end;
Auch mit dem Server kannst Du Nachrichten an den Client senden (sofern Assigned(fContext)=true):

Code: Alles auswählen

fContext.Connection.IOHandler.InputBuffer.Clear;
fContext.Connection.IOHandler.Write('TEST'+#10);


Die Rückantwort (s:String) gibt es dann mit:

Code: Alles auswählen

Try           
  s := fContext.Connection.IOHandler.ReadLn(#10,ServerReadTimeout);
  If not fContext.Connection.IOHandler.ReadLnTimedOut then begin
    ShowMessage(s);
  end else begin
    // Überprüfung auf z.B fContext.Connection.IOHandler.InputBufferIsEmpty
    // oder Untersuchung von fContext.Connection.IOHandler.InputBufferAsString
  end; 
Except
  fContext := nil;
  // ...
end;
Und der Beispielcode für den Client mit Senden über SendData und Empfang mit automatischer Anzeige:

Code: Alles auswählen

uses
 ...
  IdTCPClient;
 
type
  TClientThread = class(TThread)
    private
      fData : String;
      fTCPClient : TIdTCPClient;
      procedure SendData(AData: String);
    protected
      procedure Execute; override;
    public
      constructor Create(AHost: String; APort: Integer);
      destructor Destroy; override;
  end;
 
const 
  ClientConnectTimeout = 5000; // starte aller 5s eine neue Verbindung
 
var ClientThread : TClientThread; // Achtung: Objekt noch separat erzeugen und freigeben
 
implementation
 
procedure TClientThread.SendData(AData: String);
begin
  fTCPClient.IOHandler.Write(AData+#10);
  // ...
end;
 
procedure TClientThread.Execute;
var DataIn : String;
begin
  While not Terminated do begin
    // Starte automatisch neue Verbindung
    While not fTCPClient.Connected and not Terminated do begin
      Sleep(ClientConnectTimeout);
      Try
        fTCPClient.Connect; // verbunden mit fTCPClient.Host auf fTCPClient.Port    
      Except
         // keine Verbindung
      end;
    end;
 
    Try
      While fTCPClient.Connected and not Terminated do begin
        DataIn := fTCPClient.IOHandler.ReadLn(#10,1000);
        If not fTCPClient.IOHandler.ReadLnTimedOut then begin
          ShowMessage(DataIn);
          // ...
          // ggf. reagieren und Antwort mit SendData schicken
        end;
      end; 
    Except
      fTCPCLient.Disconnect; // wenn Disconnect während ReadLn
    end;   
  end;
  fTCPCLient.Disconnect;
end;
 
constructor TClientThread.Create(AHost: String; APort: Integer);
begin
  inherited Create(true); // Create suspended
  fTCPClient := TIdTCPCLient.Create;
  fTCPClient.Host := AHost;
  fTCPClient.Port := APort;
  Resume; // Thread automatisch starten
end;
 
destructor TClientThread.Destroy;
begin
  // ...
  fTCPClient.Free;
  Terminate;
  inherited Destroy;
end;
Ich denke, dass müsste Dir erst einmal weiterhelfen, wenn Du mit Indy weiterarbeiten möchtest.
Als Abschlusszeichen habe ich LF=#10 genommen.
Dieser spezielle Beispielcode ist allerdings nicht getestet, ich hoffe er funktioniert trotzdem :-)
Gruß Thomas

Pascal95

Re: INDY 10 | TCPServer , TCPClient

Beitrag von Pascal95 »

Vielen Dank theo, aber vor allem Thomas.

Das Beispiel ist sehr übersichtlich und erlaubt es einem so, seinen Code zu verwirklichen. Diese Basis habe ich gerade gebraucht.
Mich wundert es nur, dass solche Beispiele, die dann nicht mit Delphi, sondern mit Freepascal kompatibel sind, nicht sehr häufig öffentlich gemacht werden.
Der Code hat mir auf jeden Fall geholfen :)

Pascal

Antworten