Custom Protocol — Presence — Technical Document
sgcWebSockets · Technical Document

Custom Protocol — Presence

Presence custom subprotocol — connection-state, channels and member metadata broadcast across the room, in the spirit of Pusher Channels.

Overview

Presence protocol allows you to know who is subscribed to a channel, this makes it easier to create chat applications and know who is online, example: game users, chat rooms, users viewing the same document...

At a glance

Component class
TsgcWSPClient_Presence
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_Presence (unit sgcWebSocket_Protocols, ancestor TsgcWSProtocol_Presence_Client in unit sgcWebSocket_Protocol_Presence_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 Presence subprotocol.
BrokerOptional broker component that relays Presence messages between peers.
PresenceLocal member identity (name and arbitrary info) published to the server on join.
AcknowledgmentEnables per-message acknowledgments for Presence traffic and configures retry behavior.
EncodeBase64Base64-encodes the message payload before sending to keep binary-safe transport.
GuidUnique identifier used to route messages to a specific Presence protocol instance.
VersionRead-only Presence subprotocol version string.

Main methods

The principal public methods exposed by the component.

Subscribe()Joins the local member to the given channel.
UnSubscribe()Removes the local member from the given channel.
Publish()Publishes a text message to every member of a channel.
WriteData()Low-level raw write hook; disabled on the Presence client and kept only for API compatibility.
Invite()Invites another member by id to join a channel.

Public events

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

OnChannelInvitationFires when this member is invited to a channel; set Accept to join.
OnChannelInvitationResponseTsgcWSPClient_Presence › Events › OnChannelInvitationResponse
OnConnectFires when the underlying WebSocket transport has connected to the server.
OnDisconnectproperty OnDisconnect: TsgcWSDisconnectEvent; // TsgcWSDisconnectEvent = procedure(Connection: TsgcWSConnection; Code: Integer) of object __property TsgcWSDisconnectEvent OnDisconnect; // typedef void...
OnErrorFires when the transport reports a protocol-level error string.
OnErrorMemberChannelFires when a subscription or member-related operation fails on the server.
OnErrorPublishMsgproperty OnErrorPublishMsg: TsgcWSPresencePublishMsgErrorEvent; // TsgcWSPresencePublishMsgErrorEvent = procedure(Connection: TsgcWSConnection; const aError: TsgcWSPresenceError; const aMsg: TsgcWSPre...
OnExceptionFires when an unhandled exception is raised while processing a Presence message.
OnGetMembersFires with the list of members returned by a GetMembers request.
OnNewChannelMemberproperty OnNewChannelMember: TsgcWSPresenceNewMemberChannelEvent; // TsgcWSPresenceNewMemberChannelEvent = procedure(Connection: TsgcWSConnection; const aChannel: TsgcWSPresenceChannel; const aMember:...
OnNewMemberproperty OnNewMember: TsgcWSPresenceNewMemberEvent; // TsgcWSPresenceNewMemberEvent = procedure(aConnection: TsgcWSConnection; const aMember: TsgcWSPresenceMember) of object __property TsgcWSPresenceN...
OnPublishMsgproperty OnPublishMsg: TsgcWSPresencePublishMsgEvent; // TsgcWSPresencePublishMsgEvent = procedure(Connection: TsgcWSConnection; const aMsg: TsgcWSPresenceMsg; const aChannel: TsgcWSPresenceChannel; c...
OnRawMessageFires for every incoming frame before Presence parsing; set Handled to skip default handling.
OnRemoveChannelMemberproperty OnRemoveChannelMember: TsgcWSPresenceRemoveMemberChannelEvent; // TsgcWSPresenceRemoveMemberChannelEvent = procedure(Connection: TsgcWSConnection; const aChannel: TsgcWSPresenceChannel; const...
OnRemoveMemberproperty OnRemoveMember: TsgcWSPresenceRemoveMemberEvent; // TsgcWSPresenceRemoveMemberEvent = procedure(aConnection: TsgcWSConnection; aMember: TsgcWSPresenceMember) of object __property TsgcWSPresen...
OnSessionFires when the server returns the session id assigned to this member.

Quick Start

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

About this scenario. Pair a TsgcWSPServer_Presence with a TsgcWebSocketServer and a TsgcWSPClient_Presence with a TsgcWebSocketClient. Give the client a display name through Presence.Name, and any extra metadata through Presence.Info. After connecting, Subscribe joins a channel, Publish sends a message to everyone on it, and GetMembers asks for the current roster.

Delphi (VCL / FireMonkey)

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

// --- client side
oClient := TsgcWebSocketClient.Create(nil);
oClient.Host := '127.0.0.1';
oClient.Port := 80;
oClientPresence := TsgcWSPClient_Presence.Create(nil);
oClientPresence.Client := oClient;
oClientPresence.Presence.Name := 'alice';
oClientPresence.Presence.Info.Values['avatar'] := 'a.png';
oClientPresence.OnSession := OnPresenceSessionEvent;
oClientPresence.OnPublishMsg := OnPublishMsgEvent;
oClient.Active := True;

oClientPresence.Subscribe('room-42');
oClientPresence.Publish('hello room-42', 'room-42');
oClientPresence.GetMembers('room-42');

C++ Builder

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

// --- client side
oClient = new TsgcWebSocketClient(this);
oClient->Host = "127.0.0.1";
oClient->Port = 80;
oClientPresence = new TsgcWSPClient_Presence(this);
oClientPresence->Client = oClient;
oClientPresence->Presence->Name = "alice";
oClientPresence->OnSession = OnPresenceSessionEvent;
oClientPresence->OnPublishMsg = OnPublishMsgEvent;
oClient->Active = true;

oClientPresence->Subscribe("room-42");
oClientPresence->Publish("hello room-42", "room-42");
oClientPresence->GetMembers("room-42");

.NET (C#)

// the .NET client prefixes the presence events with OnPresence
// and delivers members and messages as plain strings
oServer = new TsgcWebSocketServer();
oServer.Port = 80;
oServerPresence = new TsgcWSPServer_Presence();
oServerPresence.Server = oServer;
oServer.Active = true;

oClient = new TsgcWebSocketClient();
oClient.Host = "127.0.0.1";
oClient.Port = 80;
oClientPresence = new TsgcWSPClient_Presence();
oClientPresence.Client = oClient;
oClientPresence.Presence.Name = "alice";
oClientPresence.OnPresenceSession += OnPresenceSessionEvent;
oClientPresence.OnPresencePublishMsg += OnPublishMsgEvent;
oClient.Active = true;

oClientPresence.Subscribe("room-42");
oClientPresence.Publish("hello room-42", "room-42");
oClientPresence.GetMembers("room-42");

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 · Who is in the room

OnNewMember and OnRemoveMember report members joining and leaving the server, OnNewChannelMember and OnRemoveChannelMember do the same for one channel. GetMembers asks for the current roster and the answer arrives on OnGetMembers, carrying a TsgcWSPresenceMemberList you can walk.

Delphi (VCL / FireMonkey)
procedure TForm1.OnNewChannelMemberEvent(Connection: TsgcWSConnection;
  const aChannel: TsgcWSPresenceChannel;
  const aMember: TsgcWSPresenceMember);
begin
  DoLog(aMember.Name + ' joined ' + aChannel.Name);
end;

procedure TForm1.OnGetMembersEvent(Connection: TsgcWSConnection;
  const aMembers: TsgcWSPresenceMemberList;
  const aChannel: TsgcWSPresenceChannel);
var
  i: Integer;
begin
  for i := 0 to aMembers.Count - 1 do
    DoLog(aMembers.Member[i].Name);
end;

oClientPresence.OnNewChannelMember := OnNewChannelMemberEvent;
oClientPresence.OnGetMembers := OnGetMembersEvent;
oClientPresence.GetMembers('room-42');
C++ Builder
void __fastcall TForm1::OnNewChannelMemberEvent(TsgcWSConnection *Connection,
  TsgcWSPresenceChannel *aChannel, TsgcWSPresenceMember *aMember)
{
  DoLog(aMember->Name + " joined " + aChannel->Name);
}

oClientPresence->OnNewChannelMember = OnNewChannelMemberEvent;
oClientPresence->OnGetMembers = OnGetMembersEvent;
oClientPresence->GetMembers("room-42");
.NET (C#)
void OnNewChannelMemberEvent(TsgcWSConnection Connection,
  string Channel, string Member)
{
  DoLog(Member + " joined " + Channel);
}

void OnGetMembersEvent(TsgcWSConnection Connection, string Channel,
  string Members)
{
  DoLog(Channel + ": " + Members);
}

oClientPresence.OnPresenceNewChannelMember += OnNewChannelMemberEvent;
oClientPresence.OnPresenceGetMembers += OnGetMembersEvent;
oClientPresence.GetMembers("room-42");

2 · Channel messages and invitations

Publish sends a message to a channel, and every subscriber receives it on OnPublishMsg with the message, the channel and the sending member. Invite asks a specific member to join a channel, the invited client sees OnChannelInvitation and answers by setting the Accept var parameter, then the inviter is told the outcome on OnChannelInvitationResponse.

Delphi (VCL / FireMonkey)
procedure TForm1.OnPublishMsgEvent(Connection: TsgcWSConnection;
  const aMsg: TsgcWSPresenceMsg;
  const aChannel: TsgcWSPresenceChannel;
  const aMember: TsgcWSPresenceMember);
begin
  DoLog(aChannel.Name + ' / ' + aMember.Name + ': ' + aMsg.Text);
end;

procedure TForm1.OnChannelInvitationEvent(Connection: TsgcWSConnection;
  const aMember: TsgcWSPresenceMember;
  const aChannel: TsgcWSPresenceChannel; var Accept: Boolean;
  var aErrorCode: Integer; var aErrorText: string);
begin
  Accept := aChannel.Name <> 'private';
  if not Accept then
  begin
    aErrorCode := 403;
    aErrorText := 'invitation refused';
  end;
end;

oClientPresence.Publish('hello room-42', 'room-42');
oClientPresence.Invite('room-42', 'bob');
C++ Builder
void __fastcall TForm1::OnPublishMsgEvent(TsgcWSConnection *Connection,
  TsgcWSPresenceMsg *aMsg, TsgcWSPresenceChannel *aChannel,
  TsgcWSPresenceMember *aMember)
{
  DoLog(aChannel->Name + " / " + aMember->Name + ": " + aMsg->Text);
}

oClientPresence->Publish("hello room-42", "room-42");
oClientPresence->Invite("room-42", "bob");
.NET (C#)
void OnPublishMsgEvent(TsgcWSConnection Connection, string Channel,
  string Message)
{
  DoLog(Channel + ": " + Message);
}

void OnChannelInvitationEvent(TsgcWSConnection Connection,
  string Channel, string From)
{
  DoLog(From + " invited you to " + Channel);
}

oClientPresence.OnPresencePublishMsg += OnPublishMsgEvent;
oClientPresence.OnPresenceChannelInvitation += OnChannelInvitationEvent;
oClientPresence.Publish("hello room-42", "room-42");
oClientPresence.Invite("room-42", "bob");

3 · Acknowledged delivery and Base64 payloads

Acknowledgment.Enabled asks the peer to confirm every presence message, with Interval and Timeout driving the retry cadence. EncodeBase64 wraps the payload so binary or multi-line text survives the JSON envelope untouched. On the server side Broadcast pushes a message to every member, optionally narrowed to one channel.

Delphi (VCL / FireMonkey)
oClientPresence.Acknowledgment.Enabled := True;
oClientPresence.Acknowledgment.Interval := 1000;
oClientPresence.Acknowledgment.Timeout := 30000;
oClientPresence.EncodeBase64 := True;

// server-side push
oServerPresence.EncodeBase64 := True;
oServerPresence.Broadcast('server restarting');
oServerPresence.Broadcast('room closing', 'room-42');
C++ Builder
oClientPresence->Acknowledgment->Enabled = true;
oClientPresence->Acknowledgment->Interval = 1000;
oClientPresence->Acknowledgment->Timeout = 30000;
oClientPresence->EncodeBase64 = true;

oServerPresence->EncodeBase64 = true;
oServerPresence->Broadcast("server restarting");
oServerPresence->Broadcast("room closing", "room-42");
.NET (C#)
oClientPresence.Acknowledgment.Enabled = true;
oClientPresence.Acknowledgment.Interval = 1000;
oClientPresence.Acknowledgment.Timeout = 30000;

oServerPresence.Broadcast("server restarting");
oServerPresence.Broadcast("room closing", "room-42");

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