WAMP Protocol — Technical Document
sgcWebSockets · Technical Document

WAMP Protocol

Routed RPC and publish-subscribe over WebSocket for Delphi, C++ Builder and .NET. WAMP v1 client and server, JSON payloads carried over the wamp WebSocket subprotocol.

Overview

WAMP is an open WebSocket subprotocol that provides two asynchronous messaging patterns: RPC and PubSub.

At a glance

Component class
TsgcWSPClient_WAMP
Standards / spec
WAMP specification (latest IETF draft)
Transports
WebSocket, WebSocket Secure
Platforms
Windows, macOS, Linux, iOS, Android
Frameworks
VCL, FireMonkey, Lazarus / FPC, .NET (client only)
Edition
Professional / Enterprise

Features

Technical specification

Standards & specsWAMP specification (latest IETF draft) · WAMP specifications index
Component classTsgcWSPClient_WAMP (unit sgcWebSocket_Protocols, ancestor TsgcWSProtocol_WAMP_Client in unit sgcWebSocket_Protocol_WAMP_Client)
FrameworksVCL, FireMonkey, Lazarus / FPC, .NET; TsgcWSPServer_WAMP is Delphi and C++ Builder only
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.

ClientReferences the TsgcWebSocketClient that carries WAMP v1 frames over a WebSocket connection.
BrokerReferences a raw-TCP broker component so the WAMP subprotocol travels over a plain socket instead of a WebSocket.
VersionRead-only string with the sgcWebSockets build version of the WAMP subprotocol component.

Main methods

The principal public methods exposed by the component.

CancelCall()Requests cancellation of an in-flight RPC previously started with Call.
Subscribe()Registers interest in a PubSub topic so that matching events are delivered to the OnEvent handler.
UnSubscribe()Cancels a prior subscription so that further events on the topic no longer reach this client.
Publish()Broadcasts an event payload to every subscriber of the given topic, with optional exclude/eligible session lists.
Call()Invokes a remote procedure identified by its URI and correlates the eventual result or error to the supplied call id.
WriteData()Sends a pre-built text or binary WAMP frame directly over the underlying WebSocket transport.
Prefix()Registers a short label that expands to a full URI, letting later Call, Subscribe and Publish frames use a compact notation.

Public events

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

OnBinaryFires when the server sends a binary WebSocket frame that is not part of the standard WAMP v1 text protocol.
OnCallErrorFires when a remote procedure invoked by Call fails on the server or is rejected.
OnCallProgressResultFires for each interim chunk of a streaming RPC before the final result arrives via OnCallResult.
OnCallResultFires once per successful RPC to deliver the final result of a Call invocation.
OnConnectFires when the underlying WebSocket (or raw TCP) transport has successfully connected to the server.
OnDisconnectFires when the underlying transport closes, ending the current WAMP session.
OnErrorFires when the component detects a transport or protocol-level error condition.
OnEventFires when a published event arrives on a topic this client has previously subscribed to.
OnExceptionFires when a Delphi exception is raised inside one of the component's worker threads or event handlers.
OnFragmentedFires for each fragment of a multi-frame WebSocket message before reassembly.
OnMessageFires for incoming text frames that the WAMP decoder did not route to a higher-level RPC or PubSub handler.
OnRawMessageFires before WAMP decoding, giving the application first look at every incoming text frame with an option to suppress further processing.
OnWelcomeFires when the server's WELCOME frame has been received, signalling that the WAMP session is fully open and ready for RPC and PubSub calls.

Quick Start

Drop the component on a form, configure the properties below and activate it. The snippet that follows shows the typical WAMP client connect configuration.

About this scenario. Pair a TsgcWSPClient_WAMP with a TsgcWebSocketClient through the Client property. The subprotocol component advertises wamp during the WebSocket handshake, so you never set a subprotocol by hand. The router answers with a WELCOME message, which surfaces as OnWelcome, and that is the first point at which it is safe to subscribe, publish or call.

Delphi (VCL / FireMonkey)

oClient := TsgcWebSocketClient.Create(nil);
oClient.Host := '127.0.0.1';
oClient.Port := 80;

oClientWAMP := TsgcWSPClient_WAMP.Create(nil);
oClientWAMP.Client := oClient;
oClientWAMP.OnWelcome := OnWelcomeEvent;
oClientWAMP.OnEvent := OnEventEvent;

oClient.Active := True;

procedure TForm1.OnWelcomeEvent(Connection: TsgcWSConnection;
  SessionId, ProtocolVersion, ServerIdent: string);
begin
  // CURIE prefix, then subscribe using the short form
  oClientWAMP.Prefix('app', 'http://example.com/topics#');
  oClientWAMP.Subscribe('app:myTopic');
end;

C++ Builder

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

oClientWAMP = new TsgcWSPClient_WAMP(this);
oClientWAMP->Client = oClient;
oClientWAMP->OnWelcome = OnWelcomeEvent;
oClientWAMP->OnEvent = OnEventEvent;

oClient->Active = true;

void __fastcall TForm1::OnWelcomeEvent(TsgcWSConnection *Connection,
  String SessionId, String ProtocolVersion, String ServerIdent)
{
  oClientWAMP->Prefix("app", "http://example.com/topics#");
  oClientWAMP->Subscribe("app:myTopic");
}

.NET (C#)

oClient = new TsgcWebSocketClient();
oClient.Host = "127.0.0.1";
oClient.Port = 80;

oClientWAMP = new TsgcWSPClient_WAMP();
oClientWAMP.Client = oClient;
oClientWAMP.OnWelcome += OnWelcomeEvent;
oClientWAMP.OnEvent += OnEventEvent;

oClient.Active = true;

void OnWelcomeEvent(TsgcWSConnection Connection, string SessionId,
  string ProtocolVersion, string ServerIdent)
{
  oClientWAMP.Prefix("app", "http://example.com/topics#");
  oClientWAMP.Subscribe("app:myTopic");
}

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 · Subscribers and publishers

A subscriber provides the topics it is interested in and then receives every event published to them, delivered through OnEvent. A publisher just calls Publish with the topic and the payload, there is no need to subscribe to a topic in order to publish on it. Publish also accepts an exclude and an eligible list so the router can filter recipients.

Delphi (VCL / FireMonkey)
procedure TForm1.OnEventEvent(Connection: TsgcWSConnection;
  TopicURI, Event: string);
begin
  DoLog(TopicURI + ': ' + Event);
end;

oClientWAMP.Subscribe('myTopic');
oClientWAMP.Publish('myTopic', 'Hello subscribers myTopic');
oClientWAMP.UnSubscribe('myTopic');
C++ Builder
void __fastcall TForm1::OnEventEvent(TsgcWSConnection *Connection,
  String TopicURI, String Event)
{
  DoLog(TopicURI + ": " + Event);
}

oClientWAMP->Subscribe("myTopic");
oClientWAMP->Publish("myTopic", "Hello subscribers myTopic");
oClientWAMP->UnSubscribe("myTopic");
.NET (C#)
void OnEventEvent(TsgcWSConnection Connection, string TopicURI,
  string Event)
{
  DoLog(TopicURI + ": " + Event);
}

oClientWAMP.Subscribe("myTopic");
oClientWAMP.Publish("myTopic", "Hello subscribers myTopic");
oClientWAMP.UnSubscribe("myTopic");

2 · Calling a remote procedure

Call takes a call id you generate, the procedure URI and an optional argument string. The router answers with a result, delivered through OnCallResult, or with an error, delivered through OnCallError. CancelCall aborts a call that is still in flight. Partial answers arrive through OnCallProgressResult, an sgcWebSockets extension to WAMP v1.

Delphi (VCL / FireMonkey)
oClientWAMP.OnCallResult := OnCallResultEvent;
oClientWAMP.OnCallError := OnCallErrorEvent;

oClientWAMP.Call('call-1', 'GetTime');
oClientWAMP.Call('call-2', 'GetProgressiveTime', '20');

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

procedure TForm1.OnCallErrorEvent(Connection: TsgcWSConnection;
  CallId, ErrorURI, ErrorDesc, ErrorDetails: string);
begin
  DoLog(CallId + ' failed: ' + ErrorDesc);
end;
C++ Builder
oClientWAMP->OnCallResult = OnCallResultEvent;
oClientWAMP->OnCallError = OnCallErrorEvent;

oClientWAMP->Call("call-1", "GetTime");
oClientWAMP->Call("call-2", "GetProgressiveTime", "20");

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

void __fastcall TForm1::OnCallErrorEvent(TsgcWSConnection *Connection,
  String CallId, String ErrorURI, String ErrorDesc, String ErrorDetails)
{
  DoLog(CallId + " failed: " + ErrorDesc);
}
.NET (C#)
oClientWAMP.OnCallResult += OnCallResultEvent;
oClientWAMP.OnCallError += OnCallErrorEvent;

oClientWAMP.Call("call-1", "GetTime");
oClientWAMP.Call("call-2", "GetProgressiveTime", "20");

void OnCallResultEvent(TsgcWSConnection Connection, string CallId,
  string Result)
{
  DoLog(CallId + " = " + Result);
}

void OnCallErrorEvent(TsgcWSConnection Connection, string CallId,
  string ErrorURI, string ErrorDesc, string ErrorDetails)
{
  DoLog(CallId + " failed: " + ErrorDesc);
}

3 · Answering calls from a WAMP server

TsgcWSPServer_WAMP turns a TsgcWebSocketServer into a WAMP v1 router. Handle OnCall, then answer with CallResult for a single result, CallProgressResult for partial results, or CallError with a call id, an error URI and an error description. Use Event to push a value to every subscriber of a topic. The server-side component ships in the Delphi and C++ Builder libraries, the .NET library provides the WAMP client only.

Delphi (VCL / FireMonkey)
oServer := TsgcWebSocketServer.Create(nil);
oServer.Port := 80;

oServerWAMP := TsgcWSPServer_WAMP.Create(nil);
oServerWAMP.Server := oServer;
oServerWAMP.OnCall := OnServerCallEvent;

oServer.Active := True;

procedure TForm1.OnServerCallEvent(Connection: TsgcWSConnection;
  const CallId, ProcUri, Arguments: string);
var
  i, vNum: Integer;
begin
  if ProcUri = 'GetTime' then
    oServerWAMP.CallResult(CallId,
      FormatDateTime('yyyymmdd hh:nn:ss', Now))
  else if ProcUri = 'GetProgressiveTime' then
  begin
    vNum := StrToInt(Arguments);
    for i := 1 to vNum do
    begin
      if i = vNum then
        oServerWAMP.CallResult(CallId,
          FormatDateTime('yyyymmdd hh:nn:ss', Now))
      else
        oServerWAMP.CallProgressResult(CallId,
          FormatDateTime('yyyymmdd hh:nn:ss', Now));
    end;
  end
  else
    oServerWAMP.CallError(CallId, 'wamp:error#notfound',
      'Unknown method');
end;
C++ Builder
oServer = new TsgcWebSocketServer(this);
oServer->Port = 80;

oServerWAMP = new TsgcWSPServer_WAMP(this);
oServerWAMP->Server = oServer;
oServerWAMP->OnCall = OnServerCallEvent;

oServer->Active = true;

void __fastcall TForm1::OnServerCallEvent(TsgcWSConnection *Connection,
  const String CallId, const String ProcUri, const String Arguments)
{
  if (ProcUri == "GetTime")
  {
    oServerWAMP->CallResult(CallId,
      FormatDateTime("yyyymmdd hh:nn:ss", Now()));
  }
  else if (ProcUri == "GetProgressiveTime")
  {
    int vNum = StrToInt(Arguments);
    for (int i = 1; i <= vNum; i++)
    {
      if (i == vNum)
        oServerWAMP->CallResult(CallId,
          FormatDateTime("yyyymmdd hh:nn:ss", Now()));
      else
        oServerWAMP->CallProgressResult(CallId,
          FormatDateTime("yyyymmdd hh:nn:ss", Now()));
    }
  }
  else
  {
    oServerWAMP->CallError(CallId, "wamp:error#notfound",
      "Unknown method");
  }
}
.NET (C#)
// TsgcWSPServer_WAMP is not part of the .NET library. Point the .NET
// WAMP client at a Delphi or C++ Builder router, or at any other
// WAMP v1 router, and consume the results through OnCallResult.
oClientWAMP.OnCallResult += OnCallResultEvent;
oClientWAMP.Call("call-1", "GetTime");

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