API SignalRCore

SignalRCore

 

ASP.NET Core SignalR 是一个开源库,可简化向应用程序添加实时 Web 功能的过程。实时 Web 功能使服务器端代码能够即时向客户端推送内容。

 

SignalR 的良好候选场景:

 

 

SignalRCore sgcWebSockets 组件使用 WebSocket 作为传输方式连接到 SignalRCore 服务器,如果此传输方式不受支持,将引发错误。

 

Hubs

SignalRCore 使用集线器(Hub)在客户端与服务器之间进行通信。SignalRCore 提供两种集线器协议:基于 JSON 的文本协议和基于 MessagePack 的二进制协议。sgcWebSockets 组件仅实现了 JSON 文本协议,用于与 SignalRCore 服务器通信。

要配置 Hub 客户端将使用哪个 Hub,只需在客户端连接到服务器之前,在 SignalRCore/Hub 属性中设置 Hub 的名称。

 

连接

当客户端向服务器打开新连接时,会发送包含格式协议和版本的请求消息。sgcWebSockets 始终以 JSON 格式发送协议。若服务器不支持该协议,将回复错误,可通过 OnSignalRCoreError 事件处理;若连接成功,则调用 OnSignalRCoreConnect 事件。

 

当客户端连接到 SignalRCore 服务器时,可以发送 ConnectionId 在会话间标识客户端。要获取新的连接 ID,只需正常连接到服务器并通过 OnBeforeConnectEvent 获知 ConnectionId。若要重连并传递之前的连接 ID,请使用 ReConnect 方法并将 ConnectionId 作为参数传入。

 

 

SignalRCore 协议

SignalR 协议是一种基于消息传输的双向 RPC 协议。连接的任意一方均可调用另一方的过程,过程可返回零个或多个结果或一个错误。示例:客户端可以请求服务器的方法,服务器也可以请求客户端的方法。服务器与客户端之间交换以下消息:

 

 

 

SignalRCore 编码

 

SignalRCore 允许使用以下编码方式:

 

 

目前仅支持 JSON,但可以使用外部 MessagePack 库对发送的消息进行编码,从而使用 MessagePack。有关更多信息,请参阅下方的 MessagePack 章节。

 

编码协议的配置在 SignalRCore.Protocol 属性中定义。默认值为 srcpJSON

 

授权

可以启用身份验证,将用户与每个连接关联,并过滤哪些用户可以访问资源。身份验证使用 Bearer 令牌实现:客户端提供访问令牌,服务器验证此令牌并使用它来识别用户。

在标准 Web API 中,承载令牌通过 HTTP 标头发送,但使用 WebSocket 时,令牌作为查询字符串参数传输。

支持以下方法:

 

srcaRequestToken

 

若启用了身份验证,流程如下:

 

1. 首先尝试从服务器获取有效令牌。打开到 Authentication.RequestToken.URL 的 HTTP 连接,并使用用户名和密码数据进行 POST。

2. 如果前一步成功,则返回令牌;否则返回错误。

3. 若返回令牌,则打开新的 HTTP 连接进行协商。此时令牌作为 HTTP 头传递。

4. 如果上一步成功,打开 WebSocket 连接并将令牌作为查询字符串参数传递。

 

 

srcaSetToken

 

在此处,您直接将令牌传递给 SignalRCore 服务器(因为令牌是从另一台服务器获取的)。

 

 

 

访问令牌可以作为查询参数发送(这是默认选项),也可以作为 Bearer Token 在 HTTP 头中发送。使用属性 Authentication.TokenParam 配置此行为。

 

 

 

srcaBasic

 

此选项使用基本身份验证,此身份验证方法需要配置 SignalRCore 组件和 TsgcWebSocketClient

 

示例:如果服务器需要基本身份验证,用户名为"user",密码为"secret",请按如下所示配置组件。

 


// websocket client
WSClient := TsgcWebSocketClient.Create(nil);
WSClient.Authentication.Enabled := True;
WSClient.Authentication.Basic.Enabled := True;
WSClient.Authentication.URL.Enabled := False;
WSClient.Authentication.Session.Enabled := False;
WSClient.Authentication.Token.Enabled := False;
WSClient.Authentication.User := 'user';
WSClient.Authentication.Password := 'secret';
// signalrcore
Signal := TsgcWSAPI_SignalRCore.Create(nil);
Signal.SignalRCore.Authentication.Enabled := True;
Signal.SignalRCore.Authentication.Authentication := srcaBasic;
Signal.SignalRCore.Authentication.Username := 'user';
Signal.SignalRCore.Authentication.Password := 'secret';
Signal.Client := WSClient;

 

客户端与服务器之间的通信

服务器与客户端之间存在三种交互类型:

 

调用

调用方向被调用方发送消息,并期待一条表明调用已完成的消息,以及调用结果(可选)

示例:客户端调用 SendMessage 方法,并将用户名和文本消息作为参数传递。发送调用 ID 以
从服务器获取结果消息。


SignalRCore.Invoke('SendMessage', ['John', 'Hello All.'], 'id-000001');
 
procedure OnSignalRCoreCompletion(Sender: TObject; Completion: TSignalRCore_Completion);
begin
  if Completion.Error <> '' then
    ShowMessage('Something goes wrong.')
  else
    ShowMessage('Invocation Successful!');
end;

非阻塞调用

调用方向被调用方发送一条消息,且不期望收到此次调用的任何后续消息。调用时可不带调用 ID 值,这表明该调用为"非阻塞"。

示例:客户端调用 SendMessage 方法,并将用户名和文本消息作为参数传入。客户端不期望服务器返回任何关于调用结果的响应。

 


SignalRCore.Invoke('SendMessage', ['John', 'Hello All.']);

流式调用

调用方向被调用方发送消息,并期望被调用方返回一个或多个结果,最后跟随一条表示调用结束的消息。

示例:客户端调用 Counter 方法,请求以 500 毫秒的间隔返回 10 个数字。


SignalRCore.InvokeStream('Counter', [10, 500], 'id-000002');
 
procedure OnSignalRCoreStreamItem(Sender: TObject; StreamItem: TSignalRCore_StreamItem; var Cancel: Boolean);
begin
  DoLog('#stream item: ' + StreamItem.Item);
end;
 
procedure OnSignalRCoreCompletion(Sender: TObject; Completion: TSignalRCore_Completion);
begin
  if Completion.Error  '' then
    ShowMessage('Something goes wrong.')
  else
    ShowMessage('Invocation Successful!');
end;

调用

为了执行单次调用,调用方遵循以下基本流程:


procedure Invoke(const aTarget: String; const aArguments: Array of Const; const aInvocationId: String = '');
procedure InvokeStream(const aTarget: String; const aArguments: Array of Const; const aInvocationId: String);

为调用分配唯一的 Invocation ID 值(由调用方选择的任意字符串)。调用包含目标对象、参数和 InvocationId 的 Invoke 或 InvokeStream 方法(如果不发送 InvocationId,则不会收到完成结果)。

 

如果调用被标记为非阻塞(参见下方"非阻塞调用"),则在此处停止并立即将控制权交还给应用程序。使用匹配的调用 ID 处理 StreamItem 或 Completion 消息。


SignalRCore.InvokeStream('Counter', [10, 500], 'id-000002');
 
procedure OnSignalRCoreStreamItem(Sender: TObject; StreamItem: TSignalRCore_StreamItem; var Cancel: Boolean);
begin
  if StreamItem.InvocationId = 'id-000002' then
    DoLog('#stream item: ' + StreamItem.Item);
end;
 
procedure OnSignalRCoreCompletion(Sender: TObject; Completion: TSignalRCore_Completion);
begin
  if StreamItem.InvocationId = 'id-000002' then
  begin
    if Completion.Error  '' then
      ShowMessage('Something goes wrong.')
    else
      ShowMessage('Invocation Successful!');
  end;
end;

您可以进行单次调用并等待完成。

 


function InvokeAndWait(const aTarget: String; aArguments: Array of Const; aInvocationId: String; out Completion: TSignalRCore_Completion; 
  const aTimeout: Integer = 10000): Boolean;
function InvokeStreamAndWait(const aTarget: String; const aArguments: Array of Const; const aInvocationId: String; 
  out Completion: TSignalRCore_Completion; const aTimeout: Integer = 10000): Boolean;

为调用分配唯一的调用 ID 值(任意字符串,由调用方选定)。调用 InvokeAndWait 或 InvokeStreamAndWait 方法,传入被调用的目标、参数和调用 ID。程序将等待直至完成事件被触发或超时。


var
  oCompletion: TSignalRCore_Completion;
begin
  if SignalRCore.InvokeStreamAndWait('Counter', [10, 500], 'id-000002', oCompletion) then
    DoLog('#invoke stream ok: ' + oCompletion.Result)
  else
    DoLog('#invocke stream error: ' + oCompletion.Error);
  
 
procedure OnSignalRCoreStreamItem(Sender: TObject; StreamItem: TSignalRCore_StreamItem; var Cancel: Boolean);
begin
  if StreamItem.InvocationId = 'id-000002' then
    DoLog('#stream item: ' + StreamItem.Item);
end;

取消调用

如果客户端希望在服务器发送 Completion 消息之前停止接收 StreamItem 消息,客户端可以发送与启动流的 StreamInvocation 消息使用相同 InvocationId 的 CancelInvocation 消息。


procedure OnSignalRCoreStreamItem(Sender: TObject; StreamItem: SignalRCore_StreamItem; var Cancel: Boolean);
begin
  if StreamItem.InvocationId = 'id-000002' then
    Cancel := True;
end;

客户端结果

 

只有收到完成消息时,调用才被视为完成。如果客户端从服务器接收到调用,将触发 OnSignalRCoreInvocation 事件。


procedure OnSignalRCoreInvocation(Sender: TObject; Invocation: TSignalRCore_Invocation);
begin
  if Invocation.Target = 'SendMessage' then
    ... your code here ...
end;
// Once invocation is completed, call Completion method to inform server invocation is finished.
// If result is successful, then call CompletionResult method:
SignalRCore.CompletionResult('id-000002', 'ok');
 
// If not, then call CompletionError method:
SignalRCore.CompletionError('id-000002', 'Error processing invocation.');

关闭连接

 

由客户端在连接关闭时发送。如果连接因错误而关闭,则包含错误原因。


SignalRCore.Close('Unexpected message').
  
// If the server close connection by any reason, OnSignalRCoreClose event will be called. 
procedure OnSignalRCoreClose(Sender: TObject; Close: TSignalRCore_Close);
begin
  DoLog('#closed: ' + Close.Error);
end;

Ping

SignalR Hub 协议支持"保活"消息,用于确保底层传输连接保持活跃。这些消息有助于确保:

在空闲期间(消息发送较少时),代理不会关闭底层连接。如果底层连接在未正常终止的情况下断开,应用程序将尽快得到通知。

保活行为通过调用 Ping 方法或在 WebSocket 客户端上启用 HeartBeat 来实现。如果服务器向客户端发送 Ping,客户端将自动发送响应,并调用 OnSignalRCoreKeepAlive 事件。


procedure OnSignalRCoreKeepAlive(Sender: TObject);
begin
  DoLog('#keepalive');
end;

MessagePack

在 SignalR 协议的 MsgPack 编码中,每条消息表示为一个单独的 MsgPack 数组,其中包含与给定 hub 协议消息属性对应的项。数组项可以是基本值、数组(例如方法参数)或对象(例如参数值)。数组中的第一项是消息类型。

 

请参阅 MessagePack 文档,了解如何对发送的消息进行编码。

 

每次收到新消息时,都会在 OnSignalRCoreMessagePack 事件中分发。消息内容可通过读取 Data Stream 参数获取。JSON 参数默认为空;如果您将 MessagePack 消息转换为 JSON,组件将像处理 JSON 编码消息一样处理该 JSON(因此 OnSignalRCoreCompletion、OnSignalRCoreInvocation 等事件将被触发)。