Custom Protocol — Dataset — Technical Document
sgcWebSockets · Technical Document

Custom Protocol — Dataset

sgcWebSockets custom subprotocol that streams TDataset changes over WebSocket — server publishes updates, clients apply them automatically.

Overview

This protocol inherits from Protocol Default and it's useful if you want to broadcast dataset changes over clients connected to this protocol. It can be used in 2 modes:

At a glance

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

Features

Technical specification

Component classTsgcWSPClient_Dataset (unit sgcWebSocket_Protocols, ancestor TsgcWSProtocol_Dataset_Client in unit sgcWebSocket_Protocol_Dataset_Client)
FrameworksVCL, FireMonkey, Lazarus / FPC
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 Dataset subprotocol.
BrokerOptional broker component that relays subprotocol messages between peers.
AutoSubscribeSubscribe automatically to the internal Dataset channels after connecting.
QoSQuality of Service options (Level, Interval, Timeout) for acknowledged delivery.
DataSetTDataSet instance that the component keeps in sync with the server.
NotifyUpdatesSend a message to the server each time the local Dataset changes.
ApplyUpdatesApply Dataset changes received from the server to the local Dataset.
UpdateModeSelects which fields are sent on update: all, changed, or full refresh.
FormatSettingsShared format settings used to serialize numeric and date/time fields.
AutoEscapeTextAutomatically escape/unescape reserved characters inside string field values.

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.
Subscribe_all()Subscribes to the three internal Dataset channels (new, update, delete).
UnSubscribe_all()Unsubscribes from the three internal Dataset channels (new, update, delete).
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.
Synchronize()Requests the full Dataset content from the server to refresh the local copy.
Broadcast()Broadcasts a message to all connected clients, optionally filtered by channel.

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.
OnAfterDeleteRecordFires after a delete received from the server has been applied to the local Dataset.
OnAfterNewRecordFires after a new record received from the server has been inserted in the local Dataset.
OnAfterSynchronizeFires when the server signals that the Synchronize batch has ended.
OnAfterUpdateRecordFires after an update received from the server has been applied to the local Dataset.
OnBeforeDatasetUpdateFires before any Dataset message from the server is applied locally; set Handled to skip it.
OnBeforeDeleteRecordFires before a delete received from the server is applied to the local Dataset.
OnBeforeNewRecordFires before a new record received from the server is inserted in the local Dataset.
OnBeforeSynchronizeFires when the server announces the start of a Synchronize batch.
OnBeforeUpdateRecordFires before an update received from the server is applied to the local Dataset.
OnBinaryFires when a binary frame arrives; payload is delivered as a TMemoryStream.
OnConnectFires after the WebSocket handshake completes and the Dataset 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...
OnMetaDataFires when the server answers a GetMetaData request with the Dataset field definitions.
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 Dataset subprotocol client and server pair configuration.

About this scenario. Bind a server-side TDataSet to a TsgcWSPServer_Dataset and the matching client-side TDataSet to a TsgcWSPClient_Dataset. Every insert, edit and delete applied on the server dataset is broadcast to the subscribed clients and replayed on their local copy. TsgcWSPServer_Dataset and TsgcWSPClient_Dataset are part of the Delphi and C++ Builder libraries, they have no .NET counterpart.

Delphi (VCL / FireMonkey)

// --- server side: bind the dataset and broadcast the changes
oServer := TsgcWebSocketServer.Create(nil);
oServer.Port := 80;

oServerDataset := TsgcWSPServer_Dataset.Create(nil);
oServerDataset.Server := oServer;
oServerDataset.DataSet := DMQuotes.cdsQuotes;
oServerDataset.NotifyUpdates := True;
oServerDataset.ApplyUpdates := True;
oServerDataset.UpdateMode := upWhereAll;
oServer.Active := True;

// --- client side: bind a local dataset and receive the changes
oClient := TsgcWebSocketClient.Create(nil);
oClient.Host := '127.0.0.1';
oClient.Port := 80;

oClientDataset := TsgcWSPClient_Dataset.Create(nil);
oClientDataset.Client := oClient;
oClientDataset.DataSet := DMQuotes.cdsQuotes;
oClientDataset.AutoSubscribe := True;
oClient.Active := True;

C++ Builder

// --- server side
oServer = new TsgcWebSocketServer(this);
oServer->Port = 80;

oServerDataset = new TsgcWSPServer_Dataset(this);
oServerDataset->Server = oServer;
oServerDataset->DataSet = DMQuotes->cdsQuotes;
oServerDataset->NotifyUpdates = true;
oServerDataset->ApplyUpdates = true;
oServerDataset->UpdateMode = upWhereAll;
oServer->Active = true;

// --- client side
oClient = new TsgcWebSocketClient(this);
oClient->Host = "127.0.0.1";
oClient->Port = 80;

oClientDataset = new TsgcWSPClient_Dataset(this);
oClientDataset->Client = oClient;
oClientDataset->DataSet = DMQuotes->cdsQuotes;
oClientDataset->AutoSubscribe = true;
oClient->Active = true;

.NET (C#)

// The Dataset subprotocol builds on TDataSet, a VCL and FireMonkey type,
// so it ships in the Delphi and C++ Builder libraries only. For a .NET
// client, publish the row changes over the sgc subprotocol instead:
oClientSGC = new TsgcWSPClient_sgc();
oClientSGC.Client = oClient;
oClientSGC.Subscribe("quotes");
oClientSGC.OnEvent += OnSGCEventEvent;

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. The Dataset subprotocol is a Delphi and C++ Builder feature, so the samples below cover those two languages.

1 · Replicating the table, and manual subscriptions

With AutoSubscribe set to True the client subscribes to every field of the bound dataset as soon as the connection is up. Leave it False and call Subscribe_all yourself, or UnSubscribe_all to stop receiving updates. Synchronize asks the server for a fresh snapshot of the whole table, and GetMetaData requests the field definitions only.

Delphi (VCL / FireMonkey)
oClientDataset.AutoSubscribe := False;
oClientDataset.OnBeforeSynchronize := OnBeforeSynchronizeEvent;
oClientDataset.OnAfterSynchronize := OnAfterSynchronizeEvent;

// after the connection is established
oClientDataset.Subscribe_all;
oClientDataset.Synchronize;
oClientDataset.GetMetaData;

// stop receiving updates
oClientDataset.UnSubscribe_all;

// on the server, push a fresh snapshot to one connection
oServerDataset.Synchronize(Connection);
C++ Builder
oClientDataset->AutoSubscribe = false;
oClientDataset->OnBeforeSynchronize = OnBeforeSynchronizeEvent;
oClientDataset->OnAfterSynchronize = OnAfterSynchronizeEvent;

oClientDataset->Subscribe_all();
oClientDataset->Synchronize();
oClientDataset->GetMetaData();

oClientDataset->UnSubscribe_all();

oServerDataset->Synchronize(Connection);

2 · Intercepting record changes

OnBeforeNewRecord, OnBeforeUpdateRecord and OnBeforeDeleteRecord fire before the incoming change is applied. Each handler receives the target dataset and the raw JSON payload, plus a Handled var parameter, set it to True and the component skips its own processing so you can apply the change yourself. The matching OnAfterNewRecord, OnAfterUpdateRecord and OnAfterDeleteRecord fire once the change has been applied.

Delphi (VCL / FireMonkey)
procedure TForm1.OnBeforeUpdateRecordEvent(Connection: TsgcWSConnection;
  const Dataset: TDataset; const JSON: IsgcObjectJSON;
  var Handled: Boolean);
begin
  // reject the update and handle it manually
  Handled := Dataset.FieldByName('READONLY').AsBoolean;
end;

procedure TForm1.OnAfterUpdateRecordEvent(Connection: TsgcWSConnection;
  const Dataset: TDataset);
begin
  DoLog('row updated');
end;

oClientDataset.OnBeforeUpdateRecord := OnBeforeUpdateRecordEvent;
oClientDataset.OnAfterUpdateRecord := OnAfterUpdateRecordEvent;
C++ Builder
void __fastcall TForm1::OnBeforeUpdateRecordEvent(TsgcWSConnection *Connection,
  TDataset *Dataset, const _di_IsgcObjectJSON JSON, bool &Handled)
{
  Handled = Dataset->FieldByName("READONLY")->AsBoolean;
}

void __fastcall TForm1::OnAfterUpdateRecordEvent(TsgcWSConnection *Connection,
  TDataset *Dataset)
{
  DoLog("row updated");
}

oClientDataset->OnBeforeUpdateRecord = OnBeforeUpdateRecordEvent;
oClientDataset->OnAfterUpdateRecord = OnAfterUpdateRecordEvent;

3 · Update mode, formatting and payload encoding

UpdateMode decides which fields go into the WHERE clause of an update, upWhereAll sends every field, upWhereChanged only the modified ones, and upRefreshAll refreshes the whole record afterwards. FormatSettings pins the date, time and decimal formats so two machines with different locales agree on the wire representation, and EncodeBase64 wraps text payloads that would otherwise need escaping.

Delphi (VCL / FireMonkey)
oServerDataset.UpdateMode := upWhereChanged;
oServerDataset.AutoSynchronize := True;
oServerDataset.NotifyUpdates := True;
oServerDataset.NotifyDeletes := True;
oServerDataset.AutoEscapeText := True;
oServerDataset.EncodeBase64 := False;
oServerDataset.FormatSettings.ShortDateFormat := 'yyyy-mm-dd';

oClientDataset.UpdateMode := upWhereChanged;
oClientDataset.ApplyUpdates := True;
oClientDataset.NotifyUpdates := True;
C++ Builder
oServerDataset->UpdateMode = upWhereChanged;
oServerDataset->AutoSynchronize = true;
oServerDataset->NotifyUpdates = true;
oServerDataset->NotifyDeletes = true;
oServerDataset->AutoEscapeText = true;
oServerDataset->EncodeBase64 = false;
oServerDataset->FormatSettings->DateFormat = "yyyy-mm-dd";

oClientDataset->UpdateMode = upWhereChanged;
oClientDataset->ApplyUpdates = true;
oClientDataset->NotifyUpdates = true;

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 — Dataset component shipped with sgcWebSockets. For full property, method and event reference consult the online help linked above.