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
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.
- Press Win+R, type in mmc and click OK to open Microsoft Management console
- Click File in the menu bar then select Add/Remove Snap-in
- Select Certificates from the Available snap ins and click Add
- Choose Computer account then click Next
- Select Local Computer then click Finish
- Click OK to add the selected snap-in to the console window.
- Go to the Personal > Certificates store (on the left), right-click on the certificate you want to export and select All Tasks > Export
- When the Certificate Export Wizard opens, click Next to proceed
- When prompted, select Yes, export the private key and press Next
- 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
- Specify the password for the PFX file and click Next
- Specify the location and name of the file where PFX will be exported. Click Next
- 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.
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. |