How to continuously send messages with TIdTCPServer?

前端 未结 2 1091
无人及你
无人及你 2021-01-14 03:50

I need to create a delphi application where when it\'s started the server is started as well and starts sending messages immediately, but I haven\'t found an example or tuto

相关标签:
2条回答
  • 2021-01-14 03:57

    Server push example application form

    This example uses a Delphi 2009 VCL application with a main form, which contains only one visual component, a TMemo named “MemoLog”.

    Client and server are both started in the FormCreate event. Note that the client code does not handle connection loss, but this can be implemented with a separate re-connect loop within the thread.

    procedure TServerPushExampleForm.FormCreate(Sender: TObject);
    begin
      ExampleServer := TMyPushServer.Create;
      ExampleServer.DefaultPort := 8088;
      ExampleServer.Active := True;
    
      ExampleClient := TMyPushClientThread.Create('localhost', 8088,
        MemoLog.Lines);
    end;
    

    Server

    The server code uses a TIdTCPCustomServer subclass which waits for a random time and then sends a string to the client.

    function TMyPushServer.DoExecute(AContext: TIdContext): Boolean;
    begin
      Result := inherited;
    
      // simulate hard work
      Sleep(Random(3000));
    
      AContext.Connection.IOHandler.WriteLn(
        'Completed at ' + TimeToStr(Now), IndyTextEncoding_UTF8);
    end;
    

    Client

    The client code uses a TThread subclass to run asynchronously without blocking the main VCL thread. It contains a private TIdTCPClient instance, and periodically tries to receive a string from the connection.

    ...
      S := TCPClient.IOHandler.ReadLn(IndyTextEncoding_UTF8);
    ...
    

    Full Delphi Form Code

    Below is the full code for the example main form.

    unit Unit1;
    
    interface
    
    uses
      IdCustomTCPServer, 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 (TIdCustomTCPServer)
      protected
        function DoExecute(AContext: TIdContext): Boolean; override;
      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 }
    
    function TMyPushServer.DoExecute(AContext: TIdContext): Boolean;
    begin
      Result := inherited;
    
      // simulate hard work
      Sleep(Random(3000));
    
      AContext.Connection.IOHandler.WriteLn(
        'Completed at ' + 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.
    

    (From https://mikejustin.wordpress.com/2014/04/19/indy-10-tidtcpserver-server-side-message-push-example/)

    0 讨论(0)
  • 2021-01-14 04:01

    The way that Indy works is to have the client (TidTCPClient) connect to the server (TidTCPServer) and then exchange data between them, back-and-forth until the connection is terminated either willfully or by premature disconnect.

    I am only referring to the actual Indy TCP components here and not to the way you see your applications.

    At the application level you might consider an application the server app and another the client app but both can/may contain both TidTCPClient and TidTCPServer components with which they communicate with other apps. This means that the server app can initiate a connection to a client app via the server app's TidTCPClient component and the client app will receive the connection via its TidTCPServer component. This would be a possible solution but keep in mind that generally clients are dynamic and ever changing while servers are usually static and as such it will be a mission to keep track of where clients are. Too many headaches and too much work as well.

    So I think it is better to have clients keep track of their rarely changing servers and as such it is better to have a TidTCPServer component for the server app and have it wait for client connections before it starts to send messages.

    So to implement; your clients would have to constantly try to connect to the server at regular intervals until it finds the server. The server can then send as many messages as it wants until asked to stop or until premature disconnect in which case the cycle will be restarted. There are ways in Indy to keep track of client connections and you can keep an internal list of the clients through those means. This makes more sense. It is the way that most client-server apps work. Just think of Skype and any Web Server. The clients contacts the server and receives data if needs be.

    At the server side:

    • Create the TidTCPServer object.
    • Setup the TidTCPServer to listen on one or more of its local IP Addresses and choose an IP port for them.
    • Assign code to the TidTCPServer which it will run as soon as a client connects to it via the OnExecute of the TidTCPServer. In this code you will send the messages to the connected client.
    • Activate the TidTCPServer so that it is in Listening mode.

    At the client side:

    • Create a TidTCPClient object.
    • Setup the TidTCPClient to use a specific host and port (The IP Address/Host Name of the server and the port you chose)
    • In a repeating loop with intervals try to connect to the server.
    • As soon as the connection is established the client may send the server something or immediatelly try to read from the connection which is what it will receive if the server sends something

    There are many examples for this type of operation. You must try first and if you struggle you can always ask questions specific to the problem you are having.

    0 讨论(0)
提交回复
热议问题