ASP.NET Core SignalR est une bibliothèque open source qui simplifie l'ajout de fonctionnalités web en temps réel aux applications. Les fonctionnalités web en temps réel permettent au code côté serveur de pousser du contenu vers les clients instantanément.
Bons candidats pour SignalR :
Le composant sgcWebSockets SignalRCore utilise WebSocket comme transport pour se connecter à un serveur SignalRCore ; si ce transport n'est pas pris en charge, une erreur sera déclenchée.
SignalRCore utilise des hubs pour communiquer entre les clients et les serveurs. SignalRCore fournit 2 protocoles de hub : un protocole texte basé sur JSON et un protocole binaire basé sur MessagePack. Le composant sgcWebSockets n'implémente que le protocole texte JSON pour communiquer avec les serveurs SignalRCore.
Pour configurer quel Hub le client utilisera, définissez simplement dans la propriété SignalRCore/Hub le nom du Hub avant que le client se connecte au serveur.
Lorsqu'un client ouvre une nouvelle connexion au serveur, il envoie un message de requête contenant le protocole de format et la version. sgcWebSockets envoie toujours le protocole de format en JSON. Le serveur répondra par une erreur si le protocole n'est pas pris en charge par le serveur ; cette erreur peut être gérée via l'événement OnSignalRCoreError. Si la connexion est réussie, l'événement OnSignalRCoreConnect sera appelé.
Lorsqu'un client se connecte à un serveur SignalRCore, il peut envoyer un ConnectionId qui identifie le client entre les sessions, donc si vous obtenez une déconnexion, le client peut se reconnecter au serveur en passant le même identifiant de connexion précédent. Pour obtenir un nouvel identifiant de connexion, connectez-vous normalement au serveur et vous pouvez connaître le ConnectionId en utilisant OnBeforeConnectEvent. Si vous souhaitez vous reconnecter au serveur et passer un identifiant de connexion précédent, utilisez la méthode ReConnect et passez ConnectionId comme paramètre.
Le protocole SignalR est un protocole pour RPC bidirectionnel sur tout transport basé sur les messages. L'une ou l'autre des parties de la connexion peut invoquer des procédures sur l'autre partie, et les procédures peuvent retourner zéro ou plusieurs résultats ou une erreur. Exemple : le client peut demander une méthode au serveur et le serveur peut demander une méthode au client. Les messages suivants sont échangés entre le serveur et les clients :
HandshakeRequest : le client envoie au serveur pour convenir du format de message.
HandshakeResponse : le serveur répond au client avec un accusé de réception du message HandshakeRequest précédent. Contient une erreur si la poignée de main a échoué.
Close : appelé par le client ou le serveur lorsqu'une connexion est fermée. Contient une erreur si la connexion a été fermée en raison d'une erreur.
Invocation : le client ou le serveur envoie un message à un autre pair pour invoquer une méthode avec ou sans arguments.
StreamInvocation : le client ou le serveur envoie un message à un autre pair pour invoquer une méthode de streaming avec ou sans arguments. La réponse sera divisée en différents éléments.
StreamItem : est une réponse à une invocation StreamInvocation précédente.
Completion : signifie qu'une invocation ou StreamInvocation précédente a été terminée. Peut contenir un résultat si le processus a réussi ou une erreur s'il y a eu une erreur.
CancelInvocation : annuler une requête StreamInvocation précédente.
Ping : est un message pour vérifier si la connexion est toujours active.
SignalRCore vous permet d'utiliser les encodages suivants :
Actuellement, seul JSON est pris en charge, bien que MessagePack puisse être utilisé en encodant les messages envoyés à l'aide d'une bibliothèque messagepack externe. Voir la section MessagePack ci-dessous pour plus d'informations.
La configuration du protocole d'encodage est définie dans la propriété SignalRCore.Protocol. Par défaut, la valeur est srcpJSON.
L'authentification peut être activée pour associer un utilisateur à chaque connexion et filtrer les utilisateurs pouvant accéder aux ressources. L'authentification est implémentée à l'aide de Bearer Tokens : le client fournit un jeton d'accès et le serveur valide ce jeton et l'utilise pour identifier l'utilisateur.
Dans les API web standard, les tokens bearer sont envoyés dans un en-tête HTTP, mais lors de l'utilisation de websockets, le token est transmis comme paramètre de chaîne de requête.
Les méthodes suivantes sont prises en charge :
srcaRequestToken
Si l'authentification est activée, le flux est le suivant :
1. Tente d'abord d'obtenir un token valide auprès du serveur. Ouvre une connexion HTTP vers Authentication.RequestToken.URL et effectue un POST avec les données User et Password.
2. Si la précédente réussit, un jeton est retourné. Sinon, une erreur est retournée.
3. Si un jeton est retourné, une nouvelle connexion HTTP est ouverte pour négocier une nouvelle connexion. Ici, le jeton est passé en tant qu'en-tête HTTP.
4. Si la précédente réussit, ouvre une connexion WebSocket et passe le jeton comme paramètre de chaîne de requête.
Authentication.Enabled : si actif, l'autorisation sera utilisée avant qu'une connexion WebSocket ne soit établie.
Authentication.Username : le nom d'utilisateur fourni au serveur pour l'authentification.
Authentication.Password : le mot de passe secret fourni au serveur pour l'authentification.
Authentication.RequestToken.PostFieldUsername: nom du champ pour transmettre le nom d'utilisateur (dépend de la configuration, consultez la page javascript HTTP pour voir quel nom est utilisé).
Authentication.RequestToken.PostFieldPassword: nom du champ pour transmettre le mot de passe (dépend de la configuration, vérifiez la page JavaScript HTTP pour voir quel nom est utilisé).
Authentication.RequestToken.URL : URL où le jeton est demandé.
Authentication.RequestToken.QueryFieldToken : nom du paramètre de chaîne de requête utilisé dans la connexion WebSocket.
srcaSetToken
Ici, vous transmettez le token directement au serveur SignalRCore (car le token a été obtenu depuis un autre serveur).
Authentication.Enabled : si actif, une autorisation sera utilisée avant l'établissement d'une connexion WebSocket.
Authentication.SetToken.Token: valeur du jeton obtenu.
Le jeton d'accès peut être envoyé comme paramètre de requête (c'est l'option par défaut) ou envoyé comme en-tête HTTP en tant que jeton Bearer. Utiliser la propriété Authentication.TokenParam pour configurer ce comportement.
srcaBasic
Cette option utilise l'authentification Basic ; cette méthode d'authentification nécessite de configurer le composant SignalRCore et le TsgcWebSocketClient.
Exemple : si le serveur requiert une authentification de base et que le nom d'utilisateur est « user » et le mot de passe « secret », configurez les composants comme indiqué ci-dessous.
// 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;
Il existe trois types d'interactions entre le serveur et les clients :
L'appelant envoie un message au destinataire et attend un message indiquant que l'invocation a été complétée et éventuellement un résultat de l'invocation
Exemple : le client invoque la méthode SendMessage et passe comme paramètres le nom d'utilisateur et le texte du message. Envoie un identifiant d'invocation pour
obtenir un message résultat du serveur.
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;
L'appelant envoie un message à l'appelé et n'attend plus aucun message pour cette invocation. Les invocations peuvent être envoyées sans valeur d'identifiant d'invocation. Cela indique que l'invocation est « non bloquante ».
Exemple : le client appelle la méthode SendMessage et passe en paramètres le nom d'utilisateur et le message texte. Le client n'attend aucune réponse du serveur concernant le résultat de l'invocation.
SignalRCore.Invoke('SendMessage', ['John', 'Hello All.']);
L'appelant envoie un message au destinataire et attend un ou plusieurs résultats renvoyés par le destinataire, suivis d'un message indiquant la fin de l'invocation.
Exemple : le client invoque la méthode Counter et demande 10 nombres avec un intervalle de 500 millisecondes.
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;
Pour effectuer une invocation unique, l'appelant suit le flux de base suivant :
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);
Allouez une valeur d'ID d'invocation unique (chaîne arbitraire, choisie par l'appelant) pour représenter l'invocation. Appelez la méthode Invoke ou InvokeStream en contenant la cible invoquée, les Arguments et l'InvocationId (si vous n'envoyez pas InvocationId, vous n'obtiendrez pas le résultat d'achèvement).
Si l'Invocation est marquée comme non bloquante (voir « Invocations non bloquantes » ci-dessous), arrêtez-vous ici et cédez immédiatement le contrôle à l'application. Traitez le message StreamItem ou Completion avec un Invocation ID correspondant.
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;
Vous pouvez effectuer un appel unique et attendre qu'il se termine.
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;
Allouez une valeur unique d'ID d'invocation (chaîne arbitraire, choisie par l'appelant) pour représenter l'invocation. Appelez InvokeAndWait ou InvokeStreamAndWait avec la méthode contenant la cible à invoquer, les arguments et l'InvocationId. Le programme attendra jusqu'à ce que l'événement de complétion soit appelé ou que le délai d'attente soit dépassé.
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;
Si le client souhaite arrêter de recevoir des messages StreamItem avant que le serveur n'envoie un message Completion, le client peut envoyer un message CancelInvocation avec le même InvocationId utilisé pour le message StreamInvocation qui a démarré le flux.
procedure OnSignalRCoreStreamItem(Sender: TObject; StreamItem: SignalRCore_StreamItem; var Cancel: Boolean);
begin
if StreamItem.InvocationId = 'id-000002' then
Cancel := True;
end;
Une invocation est considérée comme terminée uniquement lorsque le message Completion est reçu. Si le client reçoit une invocation du serveur, l'événement OnSignalRCoreInvocation sera appelé.
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.');
Envoyé par le client lorsqu'une connexion est fermée. Contient une raison d'erreur si la connexion a été fermée en raison d'une erreur.
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;
Le protocole SignalR Hub prend en charge les messages « Keep Alive » utilisés pour s'assurer que la connexion de transport sous-jacente reste active. Ces messages permettent de garantir :
Les proxies ne ferment pas la connexion sous-jacente pendant les périodes d'inactivité (lorsque peu de messages sont envoyés). Si la connexion sous-jacente est interrompue sans être fermée correctement, l'application en est informée le plus rapidement possible.
Le comportement keep alive est obtenu en appelant la méthode Ping ou en activant le HeartBeat sur le client WebSocket. Si le serveur envoie un ping au client, celui-ci envoie automatiquement une réponse et l'événement OnSignalRCoreKeepAlive est appelé.
procedure OnSignalRCoreKeepAlive(Sender: TObject);
begin
DoLog('#keepalive');
end;
Dans l'encodage MsgPack du protocole SignalR, chaque message est représenté sous forme d'un tableau MsgPack unique contenant des éléments correspondant aux propriétés du message de protocole hub donné. Les éléments du tableau peuvent être des valeurs primitives, des tableaux (par exemple des arguments de méthode) ou des objets (par exemple des valeurs d'argument). Le premier élément du tableau est le type de message.
Référez-vous à la documentation MessagePack pour voir comment encoder les messages envoyés.
Chaque fois qu'un nouveau message est reçu, il est distribué dans l'événement OnSignalRCoreMessagePack. Le message est accessible en lisant le paramètre Data Stream. Le paramètre JSON est vide par défaut ; si vous convertissez le message MessagePack en JSON, le composant traitera le message JSON comme si l'encodage utilisait JSON (donc les événements OnSignalRCoreCompletion, OnSignalRCoreInvocation... seront distribués).