ASP.NET Core SignalR은 앱에 실시간 웹 기능을 추가하는 것을 단순화하는 오픈 소스 라이브러리입니다. 실시간 웹 기능은 서버 측 코드가 클라이언트에 콘텐츠를 즉시 푸시할 수 있게 합니다.
SignalR에 적합한 후보:
SignalRCore sgcWebSockets 구성 요소는 WebSocket을 전송으로 사용하여 SignalRCore 서버에 연결하며, 이 전송이 지원되지 않으면 오류가 발생합니다.
SignalRCore는 허브를 사용하여 클라이언트와 서버 간에 통신합니다. SignalRCore는 두 가지 허브 프로토콜을 제공합니다: JSON 기반 텍스트 프로토콜과 MessagePack 기반 바이너리 프로토콜입니다. sgcWebSockets 구성 요소는 SignalRCore 서버와 통신하기 위해 JSON 텍스트 프로토콜만 구현합니다.
클라이언트가 사용할 Hub를 구성하려면, 클라이언트가 서버에 연결하기 전에 SignalRCore/Hub 속성에 Hub 이름을 설정하기만 하면 됩니다.
클라이언트가 서버에 새 연결을 열면, 형식 protocol과 버전을 포함하는 요청 메시지를 보냅니다. sgcWebSockets는 항상 형식 protocol을 JSON으로 보냅니다. 서버는 protocol이 서버에서 지원되지 않는 경우 오류로 응답하며, 이 오류는 OnSignalRCoreError 이벤트를 사용하여 처리할 수 있고, 연결이 성공하면 OnSignalRCoreConnect 이벤트가 호출됩니다.
클라이언트가 SignalRCore 서버에 연결하면 세션 간 클라이언트를 식별하는 ConnectionId를 보낼 수 있으므로, 연결 해제가 발생하면 클라이언트는 동일한 이전 connection id를 전달하여 서버에 다시 연결할 수 있습니다. 새 connection id를 얻으려면 서버에 정상적으로 연결하기만 하면 되며 OnBeforeConnectEvent를 사용하여 ConnectionId를 알 수 있습니다. 서버에 다시 연결하고 이전 connection id를 전달하려면 ReConnect 메서드를 사용하고 ConnectionId를 매개변수로 전달하십시오.
SignalR Protocol은 모든 Message 기반 전송을 통한 양방향 RPC를 위한 프로토콜입니다. 연결의 어느 당사자든 다른 당사자에서 프로시저를 호출할 수 있으며, 프로시저는 0개 이상의 결과 또는 오류를 반환할 수 있습니다. 예제: 클라이언트는 서버에서 메서드를 요청할 수 있고 서버는 클라이언트에서 메서드를 요청할 수 있습니다. 다음 메시지가 서버와 클라이언트 간에 교환됩니다:
HandshakeRequest: 클라이언트가 메시지 형식에 합의하기 위해 서버에 보냅니다.
HandshakeResponse: 서버가 클라이언트에 이전 HandshakeRequest 메시지의 확인으로 응답합니다. 핸드셰이크가 실패하면 오류를 포함합니다.
Close: 연결이 닫힐 때 클라이언트 또는 서버가 호출합니다. 오류로 인해 연결이 닫힌 경우 오류를 포함합니다.
Invocation: 클라이언트 또는 서버가 인수가 있거나 없는 메서드를 호출하기 위해 다른 피어에 메시지를 보냅니다.
StreamInvocation: 클라이언트 또는 서버가 인수가 있거나 없는 스트리밍 메서드를 호출하기 위해 다른 피어에게 메시지를 보냅니다. 응답은 다른 항목으로 분할됩니다.
StreamItem: 이전 StreamInvocation의 응답입니다.
Completion: 이전 invocation 또는 StreamInvocation이 완료되었음을 의미합니다. 프로세스가 성공한 경우 결과를 포함하거나 오류가 있는 경우 오류를 포함할 수 있습니다.
CancelInvocation: 이전 StreamInvocation 요청을 취소합니다.
Ping: 연결이 여전히 살아 있는지 확인하는 메시지입니다.
SignalRCore를 사용하면 다음 인코딩을 사용할 수 있습니다:
현재 JSON만 지원되지만, 외부 messagepack 라이브러리를 사용하여 전송되는 메시지를 인코딩함으로써 MessagePack을 사용할 수 있습니다. 자세한 내용은 아래 MessagePack 섹션을 참조하십시오.
Encoding Protocol의 구성은 SignalRCore.Protocol 속성에 정의됩니다. 기본적으로 값은 srcpJSON입니다.
각 연결에 사용자를 연결하고 어떤 사용자가 리소스에 액세스할 수 있는지 필터링하기 위해 Authentication을 활성화할 수 있습니다. Authentication은 Bearer Token을 사용하여 구현됩니다: 클라이언트가 access token을 제공하고 서버가 이 token을 검증하여 사용자를 식별하는 데 사용합니다.
표준 웹 API에서는 bearer 토큰이 HTTP 헤더로 전송되지만, websocket을 사용할 때는 토큰이 쿼리 문자열 매개변수로 전송됩니다.
다음 메서드가 지원됩니다:
srcaRequestToken
Authentication이 활성화된 경우, 흐름은 다음과 같습니다:
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: 얻은 토큰 값입니다.
액세스 토큰은 쿼리 매개변수로 전송하거나(기본 옵션) HTTP 헤더에 Bearer Token으로 전송할 수 있습니다. 이 동작을 구성하려면 Authentication.TokenParam 속성을 사용하십시오.
srcaBasic
이 옵션은 Basic Authentication을 사용하며, 이 인증 방법은 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;
서버와 클라이언트 간에는 세 가지 종류의 상호 작용이 있습니다:
Caller는 Callee에게 메시지를 보내고 호출이 완료되었음을 나타내는 메시지와 선택적으로 호출 결과를 기대합니다
예: 클라이언트가 SendMessage 메서드를 호출하고 매개변수로 사용자 이름과 텍스트 메시지를 전달합니다. 서버로부터 결과 메시지를 받기 위해
Invocation 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에 메시지를 보내고 이 호출에 대한 추가 메시지를 기대하지 않습니다. Invocation ID 값 없이 호출을 보낼 수 있습니다. 이는 호출이 "non-blocking"임을 나타냅니다.
예제: 클라이언트가 SendMessage 메서드를 호출하고 매개변수로 사용자 이름과 텍스트 메시지를 전달합니다. 클라이언트는 호출 결과에 대한 서버의 응답을 기대하지 않습니다.
SignalRCore.Invoke('SendMessage', ['John', 'Hello All.']);
Caller는 Callee에 메시지를 보내고, Callee가 반환하는 하나 이상의 결과와 그 뒤에 호출 종료를 나타내는 메시지를 기대합니다.
예: 클라이언트가 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가 선택한 임의의 문자열)을 할당하십시오. 호출되는 Target, Arguments 및 InvocationId(InvocationId를 보내지 않으면 완료 결과를 받지 못합니다)를 포함하여 Invoke 또는 InvokeStream 메서드를 호출하십시오.
Invocation이 논블로킹으로 표시된 경우(아래의 "Non-Blocking Invocations" 참조) 여기서 중지하고 즉시 애플리케이션으로 제어를 반환하십시오. 일치하는 Invocation 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;
호출을 나타내기 위해 고유한 Invocation ID 값(Caller가 선택한 임의 문자열)을 할당하십시오. 호출되는 Target, Arguments 및 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;
Invocation은 Completion 메시지가 수신될 때만 완료된 것으로 간주됩니다. 클라이언트가 서버로부터 Invocation을 받으면 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 인코딩에서, 각 Message는 주어진 hub 프로토콜 메시지의 속성에 해당하는 항목을 포함하는 단일 MsgPack 배열로 표현됩니다. 배열 항목은 기본 값, 배열(예: 메서드 인수) 또는 객체(예: 인수 값)일 수 있습니다. 배열의 첫 번째 항목은 메시지 유형입니다.
전송되는 메시지를 인코딩하는 방법을 보려면 MessagePack 문서를 참조하십시오.
새 메시지가 수신될 때마다, 이는 OnSignalRCoreMessagePack 이벤트에서 디스패치됩니다. 메시지는 Data Stream 매개변수를 읽어 액세스할 수 있습니다. 매개변수 JSON은 기본적으로 비어 있으며, MessagePack 메시지를 JSON으로 변환하면 구성 요소는 JSON 메시지를 인코딩이 JSON을 사용하는 것처럼 처리합니다(따라서 OnSignalRCoreCompletion, OnSignalRCoreInvocation... 이벤트가 디스패치됩니다).