Custom Protocol — sgc (Generic PubSub) — Technical Document
sgcWebSockets · Technical Document

Custom Protocol — sgc (Generic PubSub)

sgc generic publish-subscribe custom subprotocol — channels, queued messages and broker-style fan-out built into sgcWebSockets.

Overview

This is default sub-protocol implemented using "JSONRPC 2.0" messages, every time you send a message using this protocol, a JSON object is created with the following properties:

At a glance

Component class
TsgcWSPClient_sgc
Standards / spec
Transports
WebSocket, WebSocket Secure
Platforms
Windows, macOS, Linux, iOS, Android
Frameworks
VCL, FireMonkey, Lazarus / FPC, .NET
Edition
Professional / Enterprise

Features

Technical specification

Component classTsgcWSPClient_sgc (unit sgcWebSocket_Protocols, ancestor TsgcWSProtocol_sgc_Client in unit sgcWebSocket_Protocol_sgc_Client)
FrameworksVCL, FireMonkey, Lazarus / FPC, .NET
PlatformsWindows, macOS, Linux, iOS, Android

Main properties

The principal published / public properties used to configure and drive the component. Consult the online help for the full list.

ClientWebSocket client component used as transport for the sgc subprotocol.
BrokerOptional broker component that relays subprotocol messages between peers.
QoSQuality of Service options (Level, Interval, Timeout) for acknowledged delivery.
GuidUnique identifier used to route messages to a specific protocol instance.
VersionRead-only subprotocol version string.

Main methods

The principal public methods exposed by the component.

Subscribe()Subscribes the client to a custom channel.
UnSubscribe()Unsubscribes the client from a custom channel.
UnSubscribeAll()Unsubscribes the client from all active channel subscriptions.
Publish()Publishes a message to all clients subscribed to a channel.
StartTransaction()Begins a new transaction; subsequent messages are queued until Commit or RollBack.
WriteData()Sends a plain text message to the server using the sgc message envelope.
Broadcast()Broadcasts a message to all connected clients, optionally filtered by channel.
RPC()Sends a remote procedure call request and awaits a Result or Error response.
Notify()Sends a one-way notification to the server that does not expect a response.
Commit()Commits the current transaction so the server processes all queued messages.

Public events

The component exposes the following published events; consult the online help for full event-handler signatures.

OnAcknowledgmentFires when the server acknowledges receipt of a QoS 1 or 2 message.
OnBinaryFires when a binary frame arrives; payload is delivered as a TMemoryStream.
OnConnectFires after the WebSocket handshake completes and the sgc subprotocol is initialized.
OnDisconnectFires when the connection is closed, reporting the close code.
OnErrorproperty OnError: TsgcWSErrorEvent; // TsgcWSErrorEvent = procedure(Connection: TsgcWSConnection; const Error: string) of object __property TsgcWSErrorEvent OnError; // typedef void __fastcall (__clos...
OnEventproperty OnEvent: TsgcWSCustomEvent; // TsgcWSCustomEvent = procedure(Connection: TsgcWSConnection; const Channel, Text: string) of object __property TsgcWSCustomEvent OnEvent; // typedef void __fastc...
OnExceptionproperty OnException: TsgcExceptionEvent; // TsgcExceptionEvent = procedure(Connection: TsgcWSConnection; E: Exception) of object __property TsgcExceptionEvent OnException; // typedef void __fastcall ...
OnFragmentedFires for fragmented WebSocket frames, exposing OpCode and continuation flag.
OnMessageproperty OnMessage: TsgcWSMessageEvent; // TsgcWSMessageEvent = procedure(Connection: TsgcWSConnection; const Text: string) of object __property TsgcWSMessageEvent OnMessage; // typedef void __fastcal...
OnRPCErrorFires when the server returns an error response to an RPC request.
OnRPCResultproperty OnRPCResult: TsgcWSRPCResultEvent; // TsgcWSRPCResultEvent = procedure(Connection: TsgcWSConnection; Id, Result: string) of object __property TsgcWSRPCResultEvent OnRPCResult; // typedef void...
OnRawMessageFires before the component parses a message; set Handled to True to suppress default processing.
OnSessionFires after a successful connection or GetSession request with the assigned session Guid.
OnSubscriptionproperty OnSubscription: TsgcWSSubscriptionEvent; // TsgcWSSubscriptionEvent = procedure(Connection: TsgcWSConnection; const Subscription: String) of object __property TsgcWSSubscriptionEvent OnSubscr...
OnUnSubscriptionproperty OnUnSubscription: TsgcWSSubscriptionEvent; // TsgcWSSubscriptionEvent = procedure(Connection: TsgcWSConnection; const Subscription: String) of object __property TsgcWSSubscriptionEvent OnUnSu...

Quick Start

Drop the component on a form, configure the properties below and activate it. The snippet that follows shows the typical sgc subprotocol client and server pair configuration.

About this scenario. The sgc subprotocol is the default sgcWebSockets subprotocol, built on JSON-RPC 2.0 messages. Drop a TsgcWSPServer_sgc next to a TsgcWebSocketServer and a TsgcWSPClient_sgc next to a TsgcWebSocketClient, wire them through the Server and Client properties, and the subprotocol is advertised during the WebSocket handshake.

Delphi (VCL / FireMonkey)

// --- server side
oServer := TsgcWebSocketServer.Create(nil);
oServer.Port := 80;
oServerSGC := TsgcWSPServer_sgc.Create(nil);
oServerSGC.Server := oServer;
oServer.Active := True;

// --- client side
oClient := TsgcWebSocketClient.Create(nil);
oClient.Host := '127.0.0.1';
oClient.Port := 80;
oClientSGC := TsgcWSPClient_sgc.Create(nil);
oClientSGC.Client := oClient;
oClientSGC.OnEvent := OnSGCEventEvent;
oClient.Active := True;

// publish through a named channel
oClientSGC.Subscribe('orders');
oClientSGC.Publish('{"id":42}', 'orders');

C++ Builder

// --- server side
oServer = new TsgcWebSocketServer(this);
oServer->Port = 80;
oServerSGC = new TsgcWSPServer_sgc(this);
oServerSGC->Server = oServer;
oServer->Active = true;

// --- client side
oClient = new TsgcWebSocketClient(this);
oClient->Host = "127.0.0.1";
oClient->Port = 80;
oClientSGC = new TsgcWSPClient_sgc(this);
oClientSGC->Client = oClient;
oClientSGC->OnEvent = OnSGCEventEvent;
oClient->Active = true;

oClientSGC->Subscribe("orders");
oClientSGC->Publish("{\"id\":42}", "orders");

.NET (C#)

// --- server side
oServer = new TsgcWebSocketServer();
oServer.Port = 80;
oServerSGC = new TsgcWSPServer_sgc();
oServerSGC.Server = oServer;
oServer.Active = true;

// --- client side
oClient = new TsgcWebSocketClient();
oClient.Host = "127.0.0.1";
oClient.Port = 80;
oClientSGC = new TsgcWSPClient_sgc();
oClientSGC.Client = oClient;
oClientSGC.OnEvent += OnSGCEventEvent;
oClient.Active = true;

oClientSGC.Subscribe("orders");
oClientSGC.Publish("{\"id\":42}", "orders");

Common scenarios

Each scenario shows the configuration and method calls needed to drive the component through a specific real-world flow. Every identifier below is taken from the component declaration shipped with the library.

1 · Channels, publish and broadcast

Subscribe registers the client on a named channel, Publish sends a message to every client subscribed to that channel, and Broadcast reaches every connected client, optionally narrowed to a channel. Messages delivered on a channel arrive through OnEvent, which carries both the channel name and the payload. The optional aQueue argument selects the queue level, queueLevel0 by default.

Delphi (VCL / FireMonkey)
procedure TForm1.OnSGCEventEvent(Connection: TsgcWSConnection;
  const Channel, Text: string);
begin
  DoLog(Channel + ': ' + Text);
end;

oClientSGC.Subscribe('orders');
oClientSGC.Publish('{"id":42}', 'orders', '', queueLevel0);
oClientSGC.Broadcast('server going down in 5 min');
oClientSGC.UnSubscribe('orders');
oClientSGC.UnSubscribeAll;

// on the server, push to every subscriber of a channel
oServerSGC.Broadcast('{"tick":1}', 'orders');
C++ Builder
void __fastcall TForm1::OnSGCEventEvent(TsgcWSConnection *Connection,
  const String Channel, const String Text)
{
  DoLog(Channel + ": " + Text);
}

oClientSGC->Subscribe("orders");
oClientSGC->Publish("{\"id\":42}", "orders", "", queueLevel0);
oClientSGC->Broadcast("server going down in 5 min");
oClientSGC->UnSubscribe("orders");
oClientSGC->UnSubscribeAll();

oServerSGC->Broadcast("{\"tick\":1}", "orders");
.NET (C#)
void OnSGCEventEvent(TsgcWSConnection Connection, string Channel,
  string Text)
{
  DoLog(Channel + ": " + Text);
}

oClientSGC.Subscribe("orders");
oClientSGC.Publish("{\"id\":42}", "orders");
oClientSGC.Broadcast("server going down in 5 min");
oClientSGC.UnSubscribe("orders");
oClientSGC.UnSubscribeAll();

2 · JSON-RPC calls and notifications

RPC sends a request carrying an id you generate, a method name and an optional parameter string. The server answers through OnRPC and the result comes back to the client on OnRPCResult, or on OnRPCError when the call fails. Notify is the fire-and-forget variant, it expects no answer.

Delphi (VCL / FireMonkey)
// client
oClientSGC.OnRPCResult := OnRPCResultEvent;
oClientSGC.OnRPCError := OnRPCErrorEvent;
oClientSGC.RPC('call-1', 'getQuote', 'AAPL');
oClientSGC.Notify('ping');

procedure TForm1.OnRPCResultEvent(Connection: TsgcWSConnection;
  Id, Result: string);
begin
  DoLog(Id + ' = ' + Result);
end;

procedure TForm1.OnRPCErrorEvent(Connection: TsgcWSConnection; Id: string;
  ErrorCode: Integer; ErrorMessage, ErrorData: string);
begin
  DoLog(Id + ' failed: ' + ErrorMessage);
end;

// server
oServerSGC.OnRPC := OnServerRPCEvent;

procedure TForm1.OnServerRPCEvent(Connection: TsgcWSConnection;
  const ID, Method, Params: string);
begin
  if Method = 'getQuote' then
    oServerSGC.RPCResult(ID, '195.30')
  else
    oServerSGC.RPCError(ID, -32601, 'Method not found');
end;
C++ Builder
oClientSGC->OnRPCResult = OnRPCResultEvent;
oClientSGC->OnRPCError = OnRPCErrorEvent;
oClientSGC->RPC("call-1", "getQuote", "AAPL");
oClientSGC->Notify("ping");

void __fastcall TForm1::OnRPCResultEvent(TsgcWSConnection *Connection,
  String Id, String Result)
{
  DoLog(Id + " = " + Result);
}

void __fastcall TForm1::OnRPCErrorEvent(TsgcWSConnection *Connection,
  String Id, int ErrorCode, String ErrorMessage, String ErrorData)
{
  DoLog(Id + " failed: " + ErrorMessage);
}

oServerSGC->OnRPC = OnServerRPCEvent;

void __fastcall TForm1::OnServerRPCEvent(TsgcWSConnection *Connection,
  const String ID, const String Method, const String Params)
{
  if (Method == "getQuote")
    oServerSGC->RPCResult(ID, "195.30");
  else
    oServerSGC->RPCError(ID, -32601, "Method not found");
}
.NET (C#)
oClientSGC.OnRPCResult += OnRPCResultEvent;
oClientSGC.OnRPCError += OnRPCErrorEvent;
oClientSGC.RPC("call-1", "getQuote", "AAPL");
oClientSGC.Notify("ping");

void OnRPCResultEvent(TsgcWSConnection Connection, string RPC_ID,
  string Result)
{
  DoLog(RPC_ID + " = " + Result);
}

void OnRPCErrorEvent(TsgcWSConnection Connection, string RPC_ID,
  int ErrorCode, string ErrorMessage, string ErrorData)
{
  DoLog(RPC_ID + " failed: " + ErrorMessage);
}

// server
oServerSGC.OnRPC += OnServerRPCEvent;

void OnServerRPCEvent(TsgcWSConnection Connection, string ID,
  string Method, string Params)
{
  if (Method == "getQuote")
    oServerSGC.RPCResult(ID, "195.30");
  else
    oServerSGC.RPCError(ID, -32601, "Method not found");
}

3 · QoS and transactions

QoS.Level raises the delivery guarantee, qosLevel1 asks the peer to acknowledge every message and qosLevel2 adds the two-phase exchange. Acknowledgements surface on OnAcknowledgment. StartTransaction, Commit and RollBack queue the messages sent in between, so the server only processes them once the transaction is committed. The QoS property is published on the Delphi and C++ Builder components, the .NET client exposes the transaction methods only.

Delphi (VCL / FireMonkey)
oClientSGC.QoS.Level := qosLevel1;
oClientSGC.QoS.Interval := 1000;
oClientSGC.QoS.Timeout := 30000;

oClientSGC.StartTransaction('orders');
oClientSGC.Publish('{"id":1}', 'orders');
oClientSGC.Publish('{"id":2}', 'orders');
oClientSGC.Commit('orders');
// oClientSGC.RollBack('orders'); discards the queued messages
C++ Builder
oClientSGC->QoS->Level = qosLevel1;
oClientSGC->QoS->Interval = 1000;
oClientSGC->QoS->Timeout = 30000;

oClientSGC->StartTransaction("orders");
oClientSGC->Publish("{\"id\":1}", "orders");
oClientSGC->Publish("{\"id\":2}", "orders");
oClientSGC->Commit("orders");
.NET (C#)
oClientSGC.StartTransaction("orders");
oClientSGC.Publish("{\"id\":1}", "orders");
oClientSGC.Publish("{\"id\":2}", "orders");
oClientSGC.Commit("orders");

Sources used to build this document

Every external claim links back to a primary source. The online-help references decode the canonical deep-link the company maintains for this component.

Document scope. This document covers the publicly-documented surface of the Custom Protocol — sgc (Generic PubSub) component shipped with sgcWebSockets. For full property, method and event reference consult the online help linked above.