Custom Protocol — Presence
Presence custom subprotocol — connection-state, channels and member metadata broadcast across the room, in the spirit of Pusher Channels.
Presence custom subprotocol — connection-state, channels and member metadata broadcast across the room, in the spirit of Pusher Channels.
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...
TsgcWSPClient_Presence| Component class | TsgcWSPClient_Presence (unit sgcWebSocket_Protocols, ancestor TsgcWSProtocol_Presence_Client in unit sgcWebSocket_Protocol_Presence_Client) |
| Frameworks | VCL, FireMonkey, Lazarus / FPC, .NET |
| Platforms | Windows, macOS, Linux, iOS, Android |
The principal published / public properties used to configure and drive the component. Consult the online help for the full list.
Client | WebSocket client component used as transport for the Presence subprotocol. |
Broker | Optional broker component that relays Presence messages between peers. |
Presence | Local member identity (name and arbitrary info) published to the server on join. |
Acknowledgment | Enables per-message acknowledgments for Presence traffic and configures retry behavior. |
EncodeBase64 | Base64-encodes the message payload before sending to keep binary-safe transport. |
Guid | Unique identifier used to route messages to a specific Presence protocol instance. |
Version | Read-only Presence subprotocol version string. |
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. |
The component exposes the following published events; consult the online help for full event-handler signatures.
OnChannelInvitation | Fires when this member is invited to a channel; set Accept to join. |
OnChannelInvitationResponse | TsgcWSPClient_Presence › Events › OnChannelInvitationResponse |
OnConnect | Fires when the underlying WebSocket transport has connected to the server. |
OnDisconnect | property OnDisconnect: TsgcWSDisconnectEvent; // TsgcWSDisconnectEvent = procedure(Connection: TsgcWSConnection; Code: Integer) of object __property TsgcWSDisconnectEvent OnDisconnect; // typedef void... |
OnError | Fires when the transport reports a protocol-level error string. |
OnErrorMemberChannel | Fires when a subscription or member-related operation fails on the server. |
OnErrorPublishMsg | property OnErrorPublishMsg: TsgcWSPresencePublishMsgErrorEvent; // TsgcWSPresencePublishMsgErrorEvent = procedure(Connection: TsgcWSConnection; const aError: TsgcWSPresenceError; const aMsg: TsgcWSPre... |
OnException | Fires when an unhandled exception is raised while processing a Presence message. |
OnGetMembers | Fires with the list of members returned by a GetMembers request. |
OnNewChannelMember | property OnNewChannelMember: TsgcWSPresenceNewMemberChannelEvent; // TsgcWSPresenceNewMemberChannelEvent = procedure(Connection: TsgcWSConnection; const aChannel: TsgcWSPresenceChannel; const aMember:... |
OnNewMember | property OnNewMember: TsgcWSPresenceNewMemberEvent; // TsgcWSPresenceNewMemberEvent = procedure(aConnection: TsgcWSConnection; const aMember: TsgcWSPresenceMember) of object __property TsgcWSPresenceN... |
OnPublishMsg | property OnPublishMsg: TsgcWSPresencePublishMsgEvent; // TsgcWSPresencePublishMsgEvent = procedure(Connection: TsgcWSConnection; const aMsg: TsgcWSPresenceMsg; const aChannel: TsgcWSPresenceChannel; c... |
OnRawMessage | Fires for every incoming frame before Presence parsing; set Handled to skip default handling. |
OnRemoveChannelMember | property OnRemoveChannelMember: TsgcWSPresenceRemoveMemberChannelEvent; // TsgcWSPresenceRemoveMemberChannelEvent = procedure(Connection: TsgcWSConnection; const aChannel: TsgcWSPresenceChannel; const... |
OnRemoveMember | property OnRemoveMember: TsgcWSPresenceRemoveMemberEvent; // TsgcWSPresenceRemoveMemberEvent = procedure(aConnection: TsgcWSConnection; aMember: TsgcWSPresenceMember) of object __property TsgcWSPresen... |
OnSession | Fires when the server returns the session id assigned to this member. |
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.
// --- 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');
// --- 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");
// 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");
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.
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.
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');
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");
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");
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.
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');
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");
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");
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.
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');
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");
oClientPresence.Acknowledgment.Enabled = true; oClientPresence.Acknowledgment.Interval = 1000; oClientPresence.Acknowledgment.Timeout = 30000; oServerPresence.Broadcast("server restarting"); oServerPresence.Broadcast("room closing", "room-42");
Every external claim links back to a primary source. The online-help references decode the canonical deep-link the company maintains for this component.
Demos\02.WebSocket_Protocols\03.Presence_Protocol
.net\demos\02.WebSocket_Protocols\03.Presence_Protocol