.

WebSockets is a relatively new technology which enables full-duplex communication in web sites or web applications. A standard web application uses HTTP protocol which is a half-duplex communication protocol. Server always is in listen mode and serves incoming requests from the clients. In the conventional HTTP protocol there is no way for the server to directly send data to the clients. In order to update the client view in the browser, client should continuously ask the server and get the new updates if there are any. This method is called polling the server. Polling is normally done using a JavaScript timer which regularly sends Ajax requests to the server. If there are any new updates the data is sent back to the client.

 There are some disadvantages to use polling method:

Clients should continuously send Ajax requests to server which means server needs to handle a huge number of Ajax requests if there are too many clients.

Updates can only occur when there is an Ajax request. There is no way to update the client asynchronously. For example, if timer is set to fire each 30 seconds, client updates can only occur at 30 second intervals.

In order to overcome this limitation WebSocket technology was introduced about 10 years ago. Actually, WebSocket is not a new technology. Basically, it is just another TCP channel opened between client and the server. Unlike HTTP protocol where TCP connections can be temporary, a WebSocket connection is always open and ready to send & receive data. Client browser is able to receive messages from server through a WebSocket connection. It will enable server to send instant messages to the clients so polling mechanisms no longer will be needed.

uniGUI integrates WebSockets technology to enable developers send messages directly to web clients from the server. uniGUI implementation of WebSockets technology is very straightforward. Sending messages from server to clients can be done by using a method named BroadcastMessage(). When this method is called a message and related parameters will be broadcast through uniGUI network using WebSocket channels. This broadcast will include all Nodes and slave servers in a server farm cluster. uniGUI automatically relays the messages to all server instances which are active in a server farm cluster. Each server sends the messages to its own web clients. So no matter how complicated is your server farm cluster, uniGUI will ensure that all sessions will receive the messages.

For example using below syntax a message named 'update' will be sent to all existing clients.

BroadcastMessage('update');

 When this message is received by a web client, that client fires an Ajax request to the server. Consequently on server side an event handler named OnBroadcastMessage will be called:

procedure TMainForm.UniFormBroadcastMessage(const Sender: TComponent;  const Msg: string;
                                            const Params: TUniStrings);
begin
  if Msg = 'update' then
  begin
    UniLabel1.Caption := Query1.RecordCount.ToString;
  end;   
end;

In above example a UniLabel is updated each time a message is received. It is updated to show current record count of a database query.

The message is sent from server side and immediately reaches all the active sessions. Source of the message can be almost anywhere. It can be a user event or any other thread in the system. Evidently, messages can be sent asynchronously from any thread in the application. It is possible to use a thread timer to broadcast messages in regular time intervals. In a HyperServer cluster a message which is initiated from a Node will be propagated through all the servers in the cluster. It will be done automatically by the HyperServer no matter how big or complicated your cluster structure is.

Live Examples!

Below are few live examples running inside frames. Each frame contains a uniGUI web application session.

Chat Demo

On top two frames we have a simple chat application using WebSockets to instantly transferring chat messages to all existing sessions. You can simply test it by typing something in the related field and pressing the Send button. You will notice that the message is immediately transferred to the other frame running the chat application. You can also open new instances of this web page in new browser tabs and observe that chat messages are delivered to all chat session in open browser tabs.

Chat demo application

Chat demo application

If we examine chat application we can see that how simple is the underlying code. In below code chat message is sent to rest of the clients using the BroadcastMessage method.

procedure TMainForm.UniButton1Click(Sender: TObject);
var
  Msg, Nick : string;
begin
  Msg := Trim(UniEdit1.Text);
  if Msg <> '' then
  begin
    Nick := Trim(UniComboBox1.Text);
    // update my memo
    AddLine(Msg, Nick);

    // update other sessions' memos
    BroadcastMessage('chat',
                      [
                        'msg', Msg,
                        'nick', Nick
                      ],
    [boIgnoreCurrentSession]); // Send the message to other sessions only                
  end;
  ActiveControl := UniEdit1;
end;

The chat message is broadcasted by server to all existing web sessions using WebSocket connections. When a web clients receives a WebSocket message it casts an Ajax event to the server which will be handled in a Delphi event handler as below:

procedure TMainForm.UniFormBroadcastMessage(const Sender: TComponent;
                                            const Msg: string;
                                            const Params: TUniStrings);
begin
  if Msg = 'chat' then
    AddLine(Params['msg'].AsString, Params['nick'].AsString);
end;

 

Client Update Demos

In uniGUI implementation of WebSockets there is a possibility to update web clients directly without a need to send Ajax requests to the server. In this scenario server sends data to all sessions (or selected sessions) using a WebSocket connections. When clients receive this data a client side JavaScript handler is called to notify the client. An event handler can be used to update client side UI elements directly.

In one of the frames there is a uniGUI application showing a Label continuously refreshed with current time and date. Looking at the underlying code reveals that there is a thread timer on server side which broadcasts current date and time to all sessions.

procedure TUniServerModule.UniThreadTimer1Timer(Sender: TObject);
begin
  BroadcastMessage('update',
                  [
                    'value', FormatDateTime('dd/mm/yyyy hh:nn:ss', Now)
                  ],
                  [boClientOnly]);
end;

On the client side there is a JavaScript event handler which is responsible to get server side data and update the screen label. This method eliminates the need to send an Ajax request to the server to update the label. Label is directly updated in the client side JavaScript code. If you notice there is an option in above code named boClientOnly. This means that WinSocket message should be processed on the client side and it won't fire an Ajax request.

function form.socketmessage(sender, msg, params, eOpts)
{
   if (msg == 'update') {
      MainForm.UniLabel1.setText(params.value);
   }
}

Grid updated directly from the server

Time updated directly from the server

In the other frames there is another uniGUI application showing a grid continuously refreshed with random data from the server. It uses same principle as the previous example. There is a thread timer on server side which broadcasts an array with randomly generated data:

procedure TUniServerModule.UniThreadTimer1Timer(Sender: TObject);
var
  ParamArr : TConstArray;
  I, J : Integer;
begin
  InitConstArray(ParamArr);
  try
    for I := 1 to GRD_ROW do
      for J := 1 to GRD_COL do
        AddToConstArray(ParamArr, [Format('%d_%d', [I, J]), Random(1000)] );

    BroadcastMessage('update_grid', ParamArr, [boClientOnly]); // Message will be processed on client side event handler
  finally
    FreeConstArray(ParamArr); // This must be called to free array elements otherwise memory leak will occur
  end;
end;

As expected on the client side there's a JavaScript method which updates grid cells with received data:

function form.socketmessage(sender, msg, params, eOpts)
{
   if (msg == 'update_grid') {
      var grd = MainForm.UniStringGrid1;
      for (prop in params) {
         arr = prop.split('_');
         var row = grd.store.getAt(arr[0]); // row number
         if (row) {
            row.set(arr[1], params[prop]);
         }
      }
   }
}

Please note that directly updating the client UI is an optional feature. It is not needed to use WebSockets functionality. Most applications just do fine by updating UI in a server side Ajax request as demonstrated in the chat demo application above. Directly updating the UI is needed when you want make fast and continuous updates to the UI and don't want to create additional Ajax traffic. It helps updating UI directly from the server with little overhead and avoids creating unnecessary Ajax traffic where applicable.