【问题标题】:Use mORMot Framework to send message between server and clients使用 mORMot 框架在服务器和客户端之间发送消息
【发布时间】:2015-04-05 21:18:07
【问题描述】:

mORMot 框架 (www.synopse.info) 增加了对 WebSockets 的支持,包装上还有一个关于 WebSockets 的演示(示例 31)。在此示例中,客户端向服务器发送一条消息,然后向客户端回复一条新消息。 我想使用这个库来做到这一点:

  1. 客户端向服务器发送消息并发送IP地址(无需等待来自服务器的消息。);
  2. 服务器可以通过IP地址向单个客户端发送消息;

注意:IP 地址仅用于识别客户端。我也可以使用唯一的名称。

类似于客户端和服务器之间的 LAN 聊天。我不明白如何编辑样本 n。 31 做到这一点。此示例基于接口。

【问题讨论】:

  • 框架的链接应该是synopse.info(不是.com)

标签: delphi mormot


【解决方案1】:

无需存储 IP 或任何低级实现参数(顺便说一下,IP 无法以唯一的方式识别连接:多个客户端可能共享同一个 IP)。

mORMot框架中,异步回调是通过接口参数实现的。在服务器端,这个参数的每个实例实际上都是一个“假”类实例,链接到输入连接,能够通过其接口方法回调客户端。

这是实现回调的一种非常简单的方式——事实上,这是在服务器端实现 SOLID 回调的好方法,mORMot 框架允许在客户端/服务器中发布这种机制方式,通过使用 WebSockets

所以你先定义回调接口,以及服务接口:

  IChatCallback = interface(IInvokable)
    ['{EA7EFE51-3EBA-4047-A356-253374518D1D}']
    procedure BlaBla(const pseudo, msg: string);
  end;

  IChatService = interface(IInvokable)
    ['{C92DCBEA-C680-40BD-8D9C-3E6F2ED9C9CF}']
    procedure Join(const pseudo: string; const callback: IChatCallback);
    procedure BlaBla(const pseudo,msg: string);
    procedure CallbackReleased(const callback: IInvokable);
  end;

然后,在服务器端,每次调用IChatService.Join() 都会订阅到一个内部连接列表:

  TChatService = class(TInterfacedObject,IChatService)
  protected
    fConnected: array of IChatCallback;
  public
    procedure Join(const pseudo: string; const callback: IChatCallback);
    procedure BlaBla(const pseudo,msg: string);
    procedure CallbackReleased(const callback: IInvokable);
  end;

procedure TChatService.Join(const pseudo: string;
  const callback: IChatCallback);
begin
  InterfaceArrayAdd(fConnected,callback);
end;

然后对IChatService.BlaBla()方法的远程调用应该广播到所有连接的客户端,只需调用IChatCallback.BlaBla()方法:

procedure TChatService.BlaBla(const pseudo,msg: string);
var i: integer;
begin
  for i := 0 to high(fConnected) do
    fConnected[i].BlaBla(pseudo,msg);
end;

请注意,所有对IChatCallback.BlaBla() 的循环调用都将通过WebSockets 以异步和非阻塞方式进行,因此即使在有大量客户端的情况下,IChatService.BlaBla() 方法也是如此不会阻塞。在消息数量较多的情况下,该框架甚至能够收集推送通知消息到单个消息中,以减少资源使用。

当客户端回调实例被释放(显式,或者如果连接断开)时,服务器将调用以下方法,因此可用于取消订阅通知:

procedure TChatService.CallbackReleased(const callback: IInvokable);
begin
  InterfaceArrayDelete(fConnected,callback);
end;

在服务器端,您可以这样定义服务:

  Server.ServiceDefine(TChatService,[IChatService],sicShared).
    SetOptions([],[optExecLockedPerInterface]);

这里,optExecLockedPerInterface 选项已设置,因此所有方法调用都将是线程安全的,因此对内部fConnected[] 列表的并发访问将是安全的。

在客户端,你实现IChatCallback回调接口:

type
  TChatCallback = class(TInterfacedCallback,IChatCallback)
  protected
    procedure BlaBla(const pseudo, msg: string);
  end;

procedure TChatCallback.BlaBla(const pseudo, msg: string);
begin
  writeln(#13'@',pseudo,' ',msg);
end;

然后您订阅您的远程服务:

var Service: IChatService;
    callback: IChatCallback;
...
    Client.ServiceDefine([IChatService],sicShared);
    if not Client.Services.Resolve(IChatService,Service) then
      raise EServiceException.Create('Service IChatService unavailable');
...
      callback := TChatCallback.Create(Client,IChatCallback);
      Service.Join(pseudo,callback);
...
    try
      repeat
        TextColor(ccLightGray);
        readln(msg);
        if msg='' then
          break;
        Service.BlaBla(pseudo,msg);
      until false;
    finally
      callback := nil;
      Service := nil; // release the service local instance BEFORE Client.Free
    end;

如果您与现有的客户端/服务器 SOA 解决方案(在 Delphi、Java、C# 甚至 Go 或其他框架中)进行比较,这种基于接口的回调机制听起来非常独特且易于使用。当然,这项工作从 Delphi 6 到 XE7,也可以在 Linux 下使用,这要归功于 FPC 或 CrossKylix。

我已在我们的存储库中上传了完整的示例源代码,the server side applicationthe client side

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2022-11-29
  • 2017-05-01
  • 2021-03-05
  • 2019-03-27
  • 2015-11-09
  • 2023-01-07
  • 1970-01-01
  • 2015-01-05
相关资源
最近更新 更多