Einzelnen Beitrag anzeigen

mjustin

Registriert seit: 14. Apr 2008
3.006 Beiträge
 
Delphi 2009 Professional
 
#3

AW: Indy Server und Client

  Alt 19. Apr 2014, 09:24
Nun wollte ich auch vom Server einen Stream zum Client schicken nur der Client hat ja kein Execute Ereignis.
Habe dann bisschen gegooglet und gelesen man muss das mit einem Thread lösen.
Hab dann ein bisschen rumgebastelt und kann nun mit dem Thread so weit mal Strings empfangen.
Was allerdings nur geht wenn der Client dem Server einen String sendet und der Server das im Execute Ereignis dann zurücksendet.
Der Server verwendet das OnExecute um in einer Schleife mit dem Client zu kommunizieren. Solange der Client verbunden ist und in OnExecute keine unbehandelte Exception auftritt, wird OnExecute immer wieder ausgeführt.
Der Client hat dagegen lediglich Methoden zum Senden und Empfangen einzelner Datenobjekte (Bytes, Strings, Streams ...) an den Server.

Ein Thread ist aber nicht erforderlich, damit ein Client passiv Nachrichten empfangen kann. Der Thread ist nur dazu da, den Hauptthread nicht aufzuhalten, während der Client auf Daten wartet. In einer GUI Anwendung ist das natürlich notwendig. In einer Konsolenanwendung dagegen kann der Client dagegen im Prinzip in einer normalen while Schleife auf Nachrichten vom Server warten.

Ob mit oder ohne Thread: der Client muss nicht zuerst eine Nachricht an den Server senden, damit dieser Daten an den Client überträgt.

Wenn man zum Beispiel aktiv allen Clients regelmäßig die aktuelle Uhrzeit senden will, geht das mit diesem Code:


Delphi-Quellcode:
unit Unit1;

interface

uses
  IdTCPServer, IdTCPClient, IdContext,
  SysUtils, Classes, Forms, StdCtrls, Controls;

type
  TMyPushClientThread = class(TThread)
  private
    TCPClient: TIdTCPClient;
    FLog: TStrings;
  public
    constructor Create(AHost: string; APort: Word; ALog: TStrings);
    destructor Destroy; override;
    procedure Execute; override;
  end;

  TMyPushServer = class (TIdTCPServer)
  public
    procedure InitComponent; override;
    procedure MyOnExecute(AContext: TIdContext);
  end;

  TServerPushExampleForm = class(TForm)
    MemoLog: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    ExampleClient: TMyPushClientThread;
    ExampleServer: TMyPushServer;
  end;

var
  ServerPushExampleForm: TServerPushExampleForm;

implementation

uses
  IdGlobal;

{$R *.dfm}

procedure TServerPushExampleForm.FormCreate(Sender: TObject);
begin
  ExampleServer := TMyPushServer.Create;
  ExampleServer.DefaultPort := 8088;
  ExampleServer.Active := True;

  ExampleClient := TMyPushClientThread.Create('localhost', 8088, MemoLog.Lines);
end;

procedure TServerPushExampleForm.FormDestroy(Sender: TObject);
begin
  ExampleServer.Free;
  ExampleClient.Terminate;
  ExampleClient.WaitFor;
  ExampleClient.Free;
end;

{ TMyPushServer }

procedure TMyPushServer.InitComponent;
begin
  inherited;

  OnExecute := Self.MyOnExecute;
end;

procedure TMyPushServer.MyOnExecute(AContext: TIdContext);
begin
  Sleep(1000);
  AContext.Connection.IOHandler.WriteLn('Server-Zeit ist: ' + TimeToStr(Now), IndyTextEncoding_UTF8);
end;

{ TMyPushClientThread }

constructor TMyPushClientThread.Create(AHost: string; APort: Word; ALog: TStrings);
begin
  inherited Create(False);

  FLog := ALog;

  TCPClient := TIdTCPClient.Create;
  TCPClient.Host := AHost;
  TCPClient.Port := APort;
  TCPClient.ReadTimeout := 500;
end;

destructor TMyPushClientThread.Destroy;
begin
  TCPClient.Free;
  inherited;
end;

procedure TMyPushClientThread.Execute;
var
  S: string;
begin
  TCPClient.Connect;

  while not Terminated do
  begin
    S := TCPClient.IOHandler.ReadLn(IndyTextEncoding_UTF8);

    if not TCPClient.IOHandler.ReadLnTimedout then
    begin
      TThread.Queue(nil,
        procedure
        begin
          FLog.Append(S);
        end);
    end;

  end;

  TCPClient.Disconnect;
end;

end.
Das Grundprinzip kann man natürlich auch mit Streams verwenden (IOHandler hat Methoden um Streams und auch Files zu senden).

Die Kernfrage bei "Server Push" ist natürlich: welcher Client soll denn die Daten erhalten (wenn es mehr als ein Client ist)?

Dazu muss man sich natürlich eine eindeutige Client-Kennung bauen. "Chat"-Programme sind da ein guter Ausgangspunkt, ich habe aktuell leider keinen Link zu einem guten Tutorial parat.
Michael Justin
habarisoft.com

Geändert von mjustin (19. Apr 2014 um 14:14 Uhr) Grund: ohne IsConsole := True;
  Mit Zitat antworten Zitat