Skip to content

TTMSFNCWebSocketServer

The TTMSFNCWebSocketServer is a non-visual component enabling communication to websocket clients. It implements the RFC 6455 standard for websocket communication protocol.

Start/stop the server

Starting/stopping the server is as simple as setting the Active property.

procedure TForm1.startBtnClick(Sender: TObject);
begin
  TMSFNCWebSocketServer1.Active := True;
end;

procedure TForm1.stopBtnClick(Sender: TObject);
begin
  TMSFNCWebSocketServer1.Active := False;
end;

Allowing connections

By default all connections are allowed as long as they initiate a valid handshake request. After the handshake request is validated, the OnAllow event is triggered. This event has an AAllow: Boolean parameter which can be used to allow or disallow a connection regardless of the handshake request. The AConnection parameter gives access to the HandshakeRequest property which lets the details of the request be inspected.

procedure TForm2.TMSFNCWebSocketServer1Allow(Sender: TObject;
  AConnection: TTMSFNCWebSocketServerConnection; var aAllow: Boolean);
begin
  //Allow all connections
  AAllow := True
end;

Send data

Use BroadcastMessage(AMessage: string) or BroadcastData(AData: TBytes) to broadcast a string or binary messages to all connected clients.

procedure TForm1.broadcastBtnClick(Sender: TObject);
begin
  TMSFNCWebSocketServer1.BroadcastMessage('Hello client!');
end;

Use SendMessageTo(AMessage, ASelector) or SendDataTo(AData, ASelector) to send string or binary messages to connected clients. The ASelector callback determines which clients should the message be sent to.

//Forward incoming message to all client, except the one the message came from
procedure TForm2.TMSFNCWebSocketServer1MessageReceived(Sender: TObject;
  AConnection: TTMSFNCWebSocketConnection; const aMessage: string);
begin
  TMSFNCWebSocketServer1.SendMessageTo(AMessage, function(AClientConnection: TTMSFNCWebSocketServerConnection): Boolean
    begin
      Result := AClientConnection <> AConnection;
    end);
end;

Send in multiple frames

By default all messages are sent in one single frame. If SplitMessage is set to True, the message will be split up into multiple frames based on size defined by FrameSize. Size of the frame includes frame headers too.

Receive data/messages

When a string message arrives from one of the clients the OnMessageReceived event will be triggered. The AMessage parameter will contain the string message from the client.

When a binary message arrives from one of the clients the OnBinaryDataReceived event will be triggered. The AData parameter will contain the binary message from the client.

Ping and pong

Whenever a ping message arrives from a client the OnPing event is triggered and an automatic pong message is sent back.

If the Options property contains the twsoManualPong value the poing message is NOT sent automatically. In this case the OnPing can be used to send manually or skip it entirely.

procedure TForm2.TMSFNCWebSocketServer1Ping(Sender: TObject;
  AConnection: TTMSFNCWebSocketConnection; const aData: TArray<System.Byte>);
begin
  AConnection.SendSimpleFrame(focPong);
end;

The OnPong event is triggered whenever a pong message is received from a client.

Custom connection data

The TTMSFNCWebSocketServerConnection.UserData object property allows you to store any kind of custom data paired to your connection. It also has an OwnsUserData property which will free the object automatically if it's set to True. If it's set to False, the object needs to be released manually.

When to create/assign the UserData object is depending on the use-case. When the data that needs storing is arriving from the client itself as a form of message, then the OnMessageReceived event is the place to handle this. For example as a form of JSON message:

type
  TMyDataObject = class(TObject)
  private
    FID: string;
  public
    property ID: string read FID write FID;
  end;

procedure TForm1.TMSFNCWebSocketServer1MessageReceived(Sender: TObject;
  AConnection: TTMSFNCWebSocketConnection; const aMessage: string);
var
  json: TJSONValue;
  jObj: TJSONObject;
  t, id, s, lID: string;
  i: Integer;
begin
  json := TJSONObject.ParseJSONValue(AMessage);
  try
    if json.TryGetValue<string>('type', t) then
    begin
      if (t = 'user-data') and not Assigned(TTMSFNCWebSocketServerConnection(AConnection).UserData) then
      begin
        json.TryGetValue<string>('id', id);
        TTMSFNCWebSocketServerConnection(AConnection).OwnsUserData := True;
        TTMSFNCWebSocketServerConnection(AConnection).UserData := TMyDataObject.Create;
        TMyDataObject(TTMSFNCWebSocketServerConnection(AConnection).UserData).ID := id;
        //...
      end
      else if t = 'join' then 
      begin
        //...
      end
      //else….
  finally
    json.Free;
  end;
end;

When the server is the one that automatically assigns some data, it needs to be inspected where that should happen.

For example: The server wants to assign a unique ID for each new connection. In this case we can use the OnHandshakeResponseSent event where the server responds to the websocket handshake so the connection is estabilished.

procedure TForm1.TMSFNCWebSocketServer1HandshakeResponseSent(Sender: TObject;
  AConnection: TTMSFNCWebSocketServerConnection);
begin
  AConnection.OwnsUserData := True;
  AConnection.UserData := TMyDataObject.Create;
  TMyDataObject(AConnection.UserData).ID := GetMyUniqueID;
end;

Secure connection

Traffic over a websocket server can be protected via TLS. In order to enable this, set the UseSSL property to True.

Default OpenSSL 1.0.2 and below

The CertificateFile, CertificateKeyFile and RootCertificateFile properties can be used to define the path to the certificate file(s).

For secure connection OpenSSL libraries are required.

How to reuse an existing PFX certificate?

To convert a PFX file to a PEM file that contains both the certificate and private key, try the following command:

openssl pkcs12 -in filename.pfx -out cert.pem -nodes

Source

How to xport an existing certificate on a Windows Server

To reuse an existing certificate from IIS you can follow the steps below to export your certificate to a PFX file that you can convert to the necessary PEM format.

  1. Press Win+R, type in mmc and click OK to open Microsoft Management console
  2. Click File in the menu bar then select Add/Remove Snap-in
  3. Select Certificates from the Available snap ins and click Add
  4. Choose Computer account then click Next
  5. Select Local Computer then click Finish
  6. Click OK to add the selected snap-in to the console window.
  7. Go to the Personal > Certificates store (on the left), right-click on the certificate you want to export and select All Tasks > Export
  8. When the Certificate Export Wizard opens, click Next to proceed
  9. When prompted, select Yes, export the private key and press Next
  10. In the Export File Format dialogue window select Personal Information Exchange - PKCS #12 (.PFX) as the format you want to use. You may also check the Include all certificates in the certification path if possible option to place all intermediate certificates into the PFX file. Once done, click Next
  11. Specify the password for the PFX file and click Next
  12. Specify the location and name of the file where PFX will be exported. Click Next
  13. To complete the wizard, click the Finish button. The certificate along with the private key has been successfully exported to the PFX file when the The export was successful. message appears.

Source

OpenSSL 3.x

You can make use of the OnCreateIOHandler event to create a custom IO handler instead of the default created one. Simply look for a library or unit that builds on top of Indy TLS support (for example, TaurusTLS) and implement the event similarly to:

uses
  ..., IdServerIOHandler, ...

implementation

procedure TForm1.TMSFNCWebSocketServer1CreateIOHandler(Sender: TObject;
  var AIOHandler: TIdServerIOHandler);
begin
  AIOHandler := TCustomServerIOHandler.Create;
  TCustomServerIOHandler(AIOHandler).CustomProperty := xxx;
end;

Keep in mind

When using a custom IO handler, you need to setup all necessary properties and events manually. This applies to everything that would normally be handled via TTMSFNCWebPushServer, such as certficiate paths. Please make sure the IO handler is properly configured and events are hooked in your code if necessary.

Properties

Property name Description
Active: Boolean Start or stop the server.
CertificateFile: string Path to the certificate file. Used when UseSSL is set to True.
CertificateKeyFile: string Path to the certificate key file. Used when UseSSL is set to True.
FrameSize: UIn64 The size of each frame to be sent. Used when SplitMessage is set to True.
Options: TTMSFNCWebsocketOptions A set connection related options. Values are:
- twsoFrameByFrame: Do not assemble the frames
- twsoSkipUpgradeCheck: Do not check handshake Upgrade header
- twsoSkipVersionCheck: Do not check handshake version
- twsoManualPong: Do not send pong automatucally
- twsoManualClose: Do not send close reply automatically
PathName: string Name of the path.
For example: ws://localhost:8888/path
Port: Integer Port number. Default setting is 8888.
RootCertificateFile: string Path to the root certificate file. Used when UseSSL is set to True.
SplitMessage: Boolean If set to True, messages are split into multiple frames based on FrameSize.
UseSSL: Boolean If set to True, the server communication is protected via TLS.

Methods

Method name Description
BroadcastMessage(AMessage: string) Broadcasts a string message to all connected clients.
BroadcastData(AData: TBytes) Broadcasts a binary message to all connected clients.
SendMessageTo(AMessage: string; ASelector: TTMSFNCWebSocketSendToCallback) Sends a string message to connected clients. The ASelector callback determines to which clients the message should be sent.
SendDataTo(AData: TBytes; ASelector: TTMSFNCWebSocketSendToCallback) Sends a binary message to connected clients. The ASelector callback determines to which clients the message should be sent.

Events

Event name Description
OnAllow Event triggered to allow or disallow a client connection.
OnBinaryDataReceived Event triggered when a binary message arrives to the server.
OnClose Event triggered when a close frame is sent by the client.
OnConnect Event triggered when a clients connects to the server. This is not equal to when the client is connected (handshake successfully finished).
OnDisconnect Event triggered when a client disconnects.
OnHandshakeResponseSent Event triggered when the handshake response is sent to the client.
OnMessageReceived Event triggered when a string message arrives to the server.
OnPing Event triggered when a ping message arrives from a client.
OnPong Event triggered when a pong message arrives from a client.
OnCreateIOHandler Triggered before the default IO handler is created. Using this a custom IO handler can be created.