简体   繁体   中英

How to continuously send messages with TIdTCPServer?

我需要创建一个delphi应用程序,当它启动时,服务器也会启动并立即开始发送消息,但我还没有找到示例或教程,近5000页的Indy手册并没有让我明白我是怎么做的能做到这一点......

服务器推送示例申请表

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/ )

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM