ASP.NET Core SignalR is an open-source library that simplifies adding real-time web functionality to apps. Real-time web functionality enables server-side code to push content to clients instantly.
Good candidates for SignalR:
SignalRCore sgcWebSockets component uses WebSocket as transport to connect to a SignalRCore server, if this transport is not supported, an error will be raised.
SignalRCore uses hubs to communicate between clients and servers. SignalRCore provides 2 hub protocols: text protocol based on JSON and binary protocol based on MessagePack. The sgcWebSockets component only implements JSON text protocol to communicate with SignalRCore servers.
To configure which Hub client will use, just set in SignalRCore/Hub property the name of the Hub before the client connects to the server.
When a client opens a new connection to the server, sends a request message which contains format protocol and version. sgcWebSockets always sends format protocol as JSON. The server will reply with an error if the protocol is not supported by the server, this error can be handled using OnSignalRCoreError event, and if the connection is successful, OnSignalRCoreConnect event will be called.
When a client connects to a SignalRCore server, it can send a ConnectionId which identifies client between sessions, so if you get a disconnection client can reconnect to server passing same prior connection id. In order to get a new connection id, just connect normally to the server and you can know ConnectionId using OnBeforeConnectEvent. If you want to reconnect to the server and pass a prior connection id, use ReConnect method and pass ConnectionId as a parameter.
The SignalR Protocol is a protocol for two-way RPC over any Message-based transport. Either party in the connection may invoke procedures on the other party, and procedures can return zero or more results or an error. Example: the client can request a method from the server and server can request a method to the client. There are the following messages exchanged between server and clients:
HandshakeRequest: the client sends to the server to agree on the message format.
HandshakeResponse: server replies to the client an acknowledgement of the previous HandshakeRequest message. Contains an error if the handshake failed.
Close: called by client or server when a connection is closed. Contains an error if the connection was closed because of an error.
Invocation: client or server sends a message to another peer to invoke a method with arguments or not.
StreamInvocation: client or server sends a message to another peer to invoke a streaming method with arguments or not. The Response will be split into different items.
StreamItem: is a response from a previous StreamInvocation.
Completion: means a previous invocation or StreamInvocation has been completed. Can contain a result if the process has been successful or an error if there is some error.
CancelInvocation: cancel a previous StreamInvocation request.
Ping: is a message to check if the connection is still alive.
SignalRCore allows to use the following encodings:
Currently, only JSON is supported although MessagePack can be used encoding the messages sent using an external messagepack library. See the section MessagePack below for more information.
The configuration of the Encoding Protocol is defined in the property SignalRCore.Protocol. By default the value is srcpJSON.
Authentication can be enabled to associate a user with each connection and filter which users can access to resources. Authentication is implemented using Bearer Tokens, client provide an access token and server validates this token and uses it to identify then user.
In standard Web APIs, bearer tokens are sent in an HTTP Header, but when using websockets, token is transmitted as a query string parameter.
The following methods are supported:
srcaRequestToken
If Authentication is enabled, the flow is:
1. First tries to get a valid token from server. Opens an HTTP connection against Authentication.RequestToken.URL and do a POST using User and Password data.
2. If previous is successful, a token is returned. If not, an error is returned.
3. If token is returned, then opens a new HTTP connection to negotiate a new connection. Here, token is passed as an HTTP Header.
4. If previous is successful, opens a websocket connection and pass token as query string parameter.
Authentication.Enabled: if active, authorization will be used before a websocket connection is established.
Authentication.Username: the username provided to server to authenticate.
Authentication.Password: the secret word provided to server to authenticate.
Authentication.RequestToken.PostFieldUsername: name of field to transmit username (depends of configuration, check http javascript page to see which name is used).
Authentication.RequestToken.PostFieldPassword: name of field to transmit password (depends of configuration, check http javascript page to see which name is used).
Authentication.RequestToken.URL: url where token is requested.
Authentication.RequestToken.QueryFieldToken: name of query string parameter using in websocket connection.
srcaSetToken
Here, you pass token directly to SignalRCore server (because token has been obtained from another server).
Authentication.Enabled: if active, authorization will be used before a websocket connection is established.
Authentication.SetToken.Token: token value obtained.
The Access token can be sent as a query parameter (this is the option by default) or sent as an HTTP Header as a Bearer Token. Use the property Authentication.TokenParam to configure this behaviour.
srcaBasic
This option uses Basic Authentication, this authentication method requires to configure the SignalRCore component and the TsgcWebSocketClient.
Example: if the server requires basic authentication and the username is "user" and the password is "secret", configure the components as shown below.
// 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;
There are three kinds of interactions between server and clients:
The Caller sends a message to the Callee and expects a message indicating that the invocation has been completed and optionally a result of the invocation
Example: client invokes SendMessage method and passes as parameters user name and text message. Sends an Invocation Id to
get a result message from the server.
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;
The Caller sends a message to the Callee and does not expect any further messages for this invocation. Invocations can be sent without an Invocation ID value. This indicates that the invocation is "non-blocking".
Example: client invokes SendMessage method and passes as parameters user name and text message. The client doesn't expect any response from the server about the result of the invocation.
SignalRCore.Invoke('SendMessage', ['John', 'Hello All.']);
The Caller sends a message to the Callee and expects one or more results returned by the Callee followed by a message indicating the end of invocation.
Example: client invokes Counter method and requests 10 numbers with an interval of 500 milliseconds.
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;
In order to perform a single invocation, the Caller follows the following basic flow:
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);
Allocate a unique Invocation ID value (arbitrary string, chosen by the Caller) to represent the invocation. Call Invoke or InvokeStream method containing the Target being invoked, Arguments and InvocationId (if you don't send InvocationId, you won't get completion result).
If the Invocation is marked as non-blocking (see "Non-Blocking Invocations" below), stop here and immediately yield back to the application. Handle StreamItem or Completion message with a matching Invocation ID.
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;
You can call a single invocation and wait for completion.
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;
Allocate a unique Invocation ID value (arbitrary string, chosen by the Caller) to represent the invocation. Call InvokeAndWait or InvokeStreamAndWait method containing the Target being invoked, Arguments and InvocationId. The program will wait till completion event is called or Time out has been exceeded.
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;
If the client wants to stop receiving StreamItem messages before the Server sends a Completion message, the client can send a CancelInvocation message with the same InvocationId used for the StreamInvocation message that started the stream.
procedure OnSignalRCoreStreamItem(Sender: TObject; StreamItem: SignalRCore_StreamItem; var Cancel: Boolean);
begin
if StreamItem.InvocationId = 'id-000002' then
Cancel := True;
end;
An Invocation is only considered completed when the Completion message is received. If the client receives an Invocation from the server, OnSignalRCoreInvocation event will be called.
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.');
Sent by the client when a connection is closed. Contains an error reason if the connection was closed because of an error.
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;
The SignalR Hub protocol supports "Keep Alive" messages used to ensure that the underlying transport connection remains active. These messages help ensure:
Proxies don't close the underlying connection during idle times (when few messages are being sent). If the underlying connection is dropped without being terminated gracefully, the application is informed as quickly as possible.
Keep alive behaviour is achieved calling Ping method or enabling HeartBeat on WebSocket client. If the server sends a ping to the client, the client will send automatically a response and OnSignalRCoreKeepAlive event will be called.
procedure OnSignalRCoreKeepAlive(Sender: TObject);
begin
DoLog('#keepalive');
end;
In the MsgPack Encoding of the SignalR Protocol, each Message is represented as a single MsgPack array containing items that correspond to properties of the given hub protocol message. The array items may be primitive values, arrays (e.g. method arguments) or objects (e.g. argument value). The first item in the array is the message type.
Refer to the MessagePack documentation to see how encode the messages sent.
Every time a new message is received, this is dispatched in the event OnSignalRCoreMessagePack event. The message can be accessed reading the Data Stream parameter. The parameter JSON by default is empty, if you convert the MessagePack message to JSON, the component will process the JSON message as if the encoding was using JSON (so the events OnSignalRCoreCompletion, OnSignalRCoreInvocation... will be dispatched).