ASP.NET Core SignalR は、アプリにリアルタイム Web 機能を追加することを簡略化するオープンソースライブラリです。リアルタイム Web 機能により、サーバーサイドのコードがコンテンツをクライアントに即座にプッシュできます。
SignalRの適切な候補:
SignalRCore sgcWebSocketsコンポーネントは、SignalRCoreサーバーに接続するためのトランスポートとしてWebSocketを使用します。このトランスポートがサポートされていない場合、エラーが発生します。
SignalRCoreは、クライアントとサーバー間の通信にハブを使用します。SignalRCoreは2つのハブプロトコルを提供します: JSONベースのテキストプロトコルと、MessagePackベースのバイナリプロトコルです。sgcWebSocketsコンポーネントは、SignalRCoreサーバーとの通信にJSONテキストプロトコルのみを実装しています。
クライアントが使用する Hub を設定するには、クライアントがサーバーに接続する前に SignalRCore/Hub プロパティに Hub の名前を設定してください。
クライアントがサーバーへの新しい接続を開くと、フォーマットプロトコルとバージョンを含むリクエストメッセージを送信します。sgcWebSockets は常にフォーマットプロトコルとして JSON を送信します。サーバーはプロトコルがサポートされていない場合はエラーで返信します。このエラーは OnSignalRCoreError イベントで処理でき、接続が成功した場合は OnSignalRCoreConnect イベントが呼び出されます。
クライアントがSignalRCoreサーバーに接続する際、セッション間でクライアントを識別するConnectionIdを送信できます。切断が発生した場合、クライアントは同じConnectionIdを渡してサーバーに再接続できます。新しいConnectionIdを取得するには、通常通りサーバーに接続し、OnBeforeConnectEvent を使用してConnectionIdを確認できます。以前のConnectionIdを渡してサーバーに再接続したい場合は、ReConnect メソッドを使用してパラメータとして ConnectionId を渡してください。
SignalR プロトコルは、任意のメッセージベーストランスポートを介した双方向 RPC のプロトコルです。接続の両当事者は相手側のプロシージャを呼び出すことができ、プロシージャは 0 個以上の結果またはエラーを返すことができます。例: クライアントはサーバーからメソッドをリクエストでき、サーバーはクライアントからメソッドをリクエストできます。サーバーとクライアント間では以下のメッセージが交換されます:
HandshakeRequest: クライアントがサーバーにメッセージフォーマットに合意するために送信します。
HandshakeResponse: サーバーは以前の HandshakeRequest メッセージの確認応答をクライアントに返します。ハンドシェイクが失敗した場合はエラーが含まれます。
Close: 接続が閉じられたときにクライアントまたはサーバーによって呼び出されます。エラーにより接続が閉じられた場合はエラーを含みます。
呼び出し: クライアントまたはサーバーが別のピアにメッセージを送信し、引数ありまたはなしでメソッドを呼び出します。
StreamInvocation: クライアントまたはサーバーが、引数を伴うまたは伴わないストリーミングメソッドを呼び出すために別のピアにメッセージを送信します。レスポンスは異なるアイテムに分割されます。
StreamItem: 以前の StreamInvocation からの応答です。
Completion: 以前の呼び出しまたは StreamInvocation が完了したことを意味します。プロセスが成功した場合は結果を含み、エラーがある場合はエラーを含みます。
CancelInvocation: 以前のStreamInvocationリクエストをキャンセルします。
Ping: 接続がまだ生きているかどうかを確認するためのメッセージです。
SignalRCore では以下のエンコーディングが使用できます:
現在、JSON のみがサポートされていますが、外部の MessagePack ライブラリを使用してメッセージをエンコードすることで MessagePack を使用できます。詳細については、以下の MessagePack セクションを参照してください。
エンコーディングプロトコルの設定は SignalRCore.Protocol プロパティで定義されます。デフォルト値は srcpJSON です。
認証を有効にして、各接続にユーザーを関連付け、どのユーザーがリソースにアクセスできるかをフィルタリングできます。認証はBearer Tokenを使用して実装されます。クライアントはアクセストークンを提供し、サーバーはこのトークンを検証してユーザーを識別するために使用します。
標準的なWeb APIでは、ベアラートークンはHTTPヘッダーで送信されますが、WebSocketを使用する場合、トークンはクエリ文字列パラメータとして送信されます。
以下のメソッドがサポートされています:
srcaRequestToken
認証が有効な場合、フローは次のとおりです:
1. まず、サーバーから有効なトークンを取得しようとします。Authentication.RequestToken.URLに対してHTTP接続を開き、UserとPasswordのデータを使用してPOSTを行います。
2. 前のステップが成功した場合、トークンが返されます。そうでない場合はエラーが返されます。
3. トークンが返された場合、新しい接続をネゴシエートするために新しい HTTP 接続を開きます。ここでトークンは HTTP ヘッダーとして渡されます。
4. 前のステップが成功した場合、WebSocket 接続を開いてトークンをクエリ文字列パラメーターとして渡します。
Authentication.Enabled: 有効な場合、WebSocket 接続が確立される前に認可が使用されます。
Authentication.Username: 認証のためにサーバーに提供されるユーザー名。
Authentication.Password: 認証のためにサーバーに提供するシークレットワード。
Authentication.RequestToken.PostFieldUsername: ユーザー名を送信するフィールドの名前(設定によって異なります。使用される名前については HTTP JavaScript ページを確認してください)。
Authentication.RequestToken.PostFieldPassword: パスワードを送信するフィールドの名前(構成によって異なります。どの名前が使用されているかを確認するにはhttp javascriptページを確認してください)。
Authentication.RequestToken.URL: トークンがリクエストされる URL。
Authentication.RequestToken.QueryFieldToken: websocket接続で使用するクエリ文字列パラメータの名前です。
srcaSetToken
ここでは、別のサーバーから取得したトークンを SignalRCore サーバーに直接渡します。
Authentication.Enabled: アクティブな場合、WebSocket 接続が確立される前に認証が使用されます。
Authentication.SetToken.Token: 取得したトークンの値。
アクセストークンはクエリパラメータとして送信できます(デフォルトのオプション)。または Bearer トークンとして HTTP ヘッダーで送信することもできます。Authentication.TokenParam プロパティを使用してこの動作を設定します。
srcaBasic
このオプションは Basic 認証を使用します。この認証メソッドでは SignalRCore コンポーネントと TsgcWebSocketClient の設定が必要です。
使用例: サーバーが Basic 認証を必要とし、ユーザー名が "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;
サーバーとクライアント間には3種類のやり取りがあります:
呼び出し元は呼び出し先にメッセージを送信し、呼び出しが完了したことを示すメッセージと、オプションで呼び出しの結果を期待します。
使用例: クライアントが 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;
Caller は Callee にメッセージを送信し、この呼び出しに対してそれ以上のメッセージを期待しません。呼び出しは呼び出し ID 値なしで送信できます。これは呼び出しが「ノンブロッキング」であることを示します。
例: クライアントが SendMessage メソッドを呼び出し、パラメーターとしてユーザー名とテキストメッセージを渡します。クライアントは呼び出しの結果についてサーバーからのレスポンスを期待しません。
SignalRCore.Invoke('SendMessage', ['John', 'Hello All.']);
Caller は Callee にメッセージを送信し、Callee から 1 つ以上の結果が返されることを期待し、その後に呼び出しの終了を示すメッセージが返されます。
例: クライアントが 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;
単一の呼び出しを実行するために、Caller は次の基本フローに従います:
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 値(任意の文字列、Caller が選択)を割り当てます。呼び出されるターゲット、Arguments、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 値(任意の文字列、呼び出し元が選択)を割り当てます。呼び出されるターゲット、引数、および InvocationId を含む InvokeAndWait または InvokeStreamAndWait メソッドを呼び出します。プログラムは完了イベントが呼び出されるかタイムアウトが超過するまで待機します。
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;
SignalR Hubプロトコルは、基盤となるトランスポート接続がアクティブのままであることを保証するために使用される「Keep Alive」メッセージをサポートしています。これらのメッセージは次を保証するのに役立ちます:
プロキシがアイドル時(送信されるメッセージが少ないとき)に基盤となる接続を閉じないこと。基盤となる接続が正常に終了されずに切断された場合、アプリケーションにできるだけ早く通知されます。
Keep Aliveの動作は、Pingメソッドを呼び出すか、WebSocketクライアントでHeartBeatを有効にすることで実現されます。サーバーがクライアントにpingを送信すると、クライアントは自動的に応答を送信し、OnSignalRCoreKeepAliveイベントが呼び出されます。
procedure OnSignalRCoreKeepAlive(Sender: TObject);
begin
DoLog('#keepalive');
end;
SignalR プロトコルのMsgPackエンコーディングでは、各メッセージは指定されたハブプロトコルメッセージのプロパティに対応するアイテムを含む単一のMsgPack配列として表されます。配列内のアイテムはプリミティブ値、配列(例:メソッド引数)、またはオブジェクト(例:引数値)である場合があります。配列の最初のアイテムはメッセージタイプです。
送信されたメッセージのエンコード方法については MessagePack ドキュメントを参照してください。
新しいメッセージを受信するたびに、OnSignalRCoreMessagePack イベントでディスパッチされます。メッセージは Data ストリームパラメーターを読み取ることでアクセスできます。デフォルトでは JSON パラメーターは空です。MessagePack メッセージを JSON に変換すると、コンポーネントはエンコードが JSON を使用していた場合と同様に JSON メッセージを処理します(OnSignalRCoreCompletion、OnSignalRCoreInvocation などのイベントがディスパッチされます)。