API SignalRCore

SignalRCore

 

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.

 

Hubs

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.

 

Connection

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.

 

 

SignalRCore Protocol

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:

 

 

 

SignalRCore Encoding

 

SignalRCore allows you 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.

 

Authorization

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.

 

 

srcaSetToken

 

Here, you pass token directly to SignalRCore server (because token has been obtained from another server).

 

 

 

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
TsgcWebSocketClient* WSClient = new TsgcWebSocketClient();
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
TsgcWSAPI_SignalRCore* Signal = new TsgcWSAPI_SignalRCore();
Signal->SignalRCore->Authentication->Enabled = true;
Signal->SignalRCore->Authentication->Authentication = srcaBasic;
Signal->SignalRCore->Authentication->Username = "user";
Signal->SignalRCore->Authentication->Password = "secret";
Signal->Client = WSClient;

 

Communication between Client an Server

There are three kinds of interactions between server and clients:  

 

Invocations

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", ARRAYOFCONST(("John", "Hello All.")), "id-000001");
 
void OnSignalRCoreCompletion(TObject *Sender, TSignalRCore_Completion *Completion)
{
  if (Completion->Error != "") 
  {
    ShowMessage("Something goes wrong.")
  }
  else
  {
    ShowMessage("Invocation Successful!");
  }
}

Non-Blocking Invocations

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", ARRAYOFCONST(("John", "Hello All.")));

Streaming Invocations

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");
 
void OnSignalRCoreStreamItem(TObject *Sender, TSignalRCore_StreamItem *StreamItem, ref bool Cancel);
{
  DoLog("#stream item: " + StreamItem->Item);
}
 
void OnSignalRCoreCompletion(TObject *Sender, TSignalRCore_Completion *Completion)
{
  if (Completion->Error != "") 
  {
    ShowMessage("Something goes wrong.")
  }
  else
  {
    ShowMessage("Invocation Successful!");
  }
}

Invocations

In order to perform a single invocation, the Caller follows the following basic flow:


void Invoke(const string aTarget, const Array of Const aArguments, const string aInvocationId)
void InvokeStream(const string aTarget, const Array of Const aArguments, const String aInvocationId)

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");
 
void OnSignalRCoreStreamItem(TObject *Sender, TSignalRCore_StreamItem *StreamItem, ref bool Cancel)
{
  if (StreamItem->InvocationId == "id-000002")
  {
    DoLog("#stream item: " + StreamItem->Item);
  }
}
 
void OnSignalRCoreCompletion(TObject *Sender, TSignalRCore_Completion *Completion)
{
  if (StreamItem->InvocationId == "id-000002")
  {
    if (Completion->Error != "") 
    {
      ShowMessage("Something goes wrong.")
    }
    else
    {
      ShowMessage("Invocation Successful!");
    }
  }
}

You can call a single invocation and wait for completion.

 


bool InvokeAndWait(const String aTarget, System::TVarRec *aArguments, string aInvocationId, 
  out TSignalRCore_Completion *Completion, const int aTimeout = 10000)
bool InvokeStreamAndWait(const String aTarget, System::TVarRec *aArguments, string aInvocationId, 
  out TSignalRCore_Completion *Completion, const int aTimeout = 10000)

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. 


{
  TSignalRCore_Completion oCompletion;
  if (SignalRCore->InvokeStreamAndWait("Counter", ARRAYOFCONST((10, 500)), "id-000002", oCompletion))
  {
    DoLog("#invoke stream ok: " + oCompletion->Result)
  }
  else
  {
    DoLog("#invocke stream error: " + oCompletion->Error);
  }
}
  
 
void OnSignalRCoreStreamItem(TObject *Sender, TSignalRCore_StreamItem *StreamItem, ref bool Cancel)
{
  if (StreamItem->InvocationId == "id-000002")
  {
    DoLog("#stream item: " + StreamItem->Item);
  }
}

Cancel Invocation

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.


void OnSignalRCoreStreamItem(TObject *Sender, SignalRCore_StreamItem *StreamItem, ref bool Cancel)
{
  if (StreamItem->InvocationId == "id-000002")
  {
    Cancel = true;
  }
}

Client Results

 

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.


void OnSignalRCoreInvocation(TObject *Sender, TSignalRCore_Invocation *Invocation)
{
  if (Invocation->Target == "SendMessage")
  {
    ... your code here ...
  }
}
 
// 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.");

Close Connection

 

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.
void OnSignalRCoreClose(TObject *Sender, TSignalRCore_Close *Close)
{
  DoLog("#closed: " + Close->Error);
}

Ping

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.


void OnSignalRCoreKeepAlive(TObject *Sender)
{
  DoLog("#keepalive");
}

MessagePack

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