WebSocket tutorial

Based on the W3C specification, how to exchange data with a server and a client, from a server program.

WebSocket is an alternative to Ajax simpler to implement client side, but with limited compatibility with browsers. The complete protocol is supported by Internet Explorer 10, Chrome since version 16, Firefox since version 11, Safari since 6.0.

But why use WebSocket? Unlike the XMLHttpRequest object of Ajax, which sends requests to the server and updates the web page asynchronously when a script on the server returns the results, WebSocket can send data to the page on the initiative of the server. One can start a process on the server - or several simultaneous processes - that will send data to a browser running as a dashboard with various widgets that present the information received...
There are of course other ways to use a web page as an application interface, for example installing a framework that supports data binding (React, Angular, Polymer, etc.), but WebSocket is simpler and works without the need to include a large size library.

The standard client-side API

The W3C describes the WebSocket object and methods that are now standard on all browsers.

An instance is declared with the URL for parameter containing the ws: protocol for a simple connection or wss: for a secure connection.

var ws = new WebSocket("ws://www.example.com");

It could also be a local connection, in this case we also specify the port, for example 8100.

var ws = new WebSocket("ws://localhost:8100");

The object sends data to the server as a string with the send method.

ws.send("Hello server...");

But it also allows you to send structured data, simply convert them to a string for the transmission.

var data = { "type" : "text", "message" : "Hello" };
var message = JSON.stringify(data);
ws.send(message);

The object responds to messages from the server with three event handlers.

  1. onopen detects the opening of a connection by the server.
  2. onmessage receives the data.
  3. onclose detects the closing of the connection by the server.

Onopen

The connection is established.

socket.onopen = function (event) {      
  socket.send('{ "type": "text", "message": "Ready" }' );     
};

Something is done when the server is connected, such as sending a message to indicate that the browser is ready to receive data.
Note that instead of using stringify, you can simply put the object in quotes!

Onclose

The server cutted the connection.

socket.onclose = function (event) {      
  alert("End of communication");
};

Here too we do something in response to the event, such as displaying a warning.

Onmessage

The server sent data.

socket.onmessage=function(event) { 
var data = JSON.parse(event.data); switch(data.type) { case "text": document.getElementById("message").innerHTML = data.message; break; }
};

It is assumed that the server also sends structured data, so the received string event.data is converted in JavaScript object with the method JSON.parse.

We now have an object with the type property like that was sent to the server and the message property whose value is a text that we, in the example, inserts in the page in a layer with the ID "message".

<div id="message"></div>

The object can offer several types along with different types of data, that is discussed below.

Close method

The browser itself can also close the communication. It will use the close method, that will be detected by the server.

ws.close();

There are no open method, creating an instance of WebSocket is enough to open a connection.

Server side in JavaScript

Server side, you have the choice of language provided that it supports the WebSocket protocol. The advantage of JavaScript is that you can easily install it locally on your computer for development. Easier than PHP or another language, even if they also offer their solution.
We can write yourself a server in JavaScript running on Node.js, but in fact there are many libraries ready to use, from the simplest to the more complex.

The server side code depends on the framework that we have chosen. Here is an example made with ws.

  1. Install Node.js.
  2. Install the framework:
    npm install --save ws
  3. Server side, launch the wsserver.js file:
    node wsserver.js
  4. On the client side, loading the wsclient.html page in a browser . That's all!

The connection is established automatically.

We will exchange messages between the client and server that are objects passed as string. Simply place the object in quotes.

{ 
  "type":"text", 
  "content":"Some message..."
}

The method JSON.parse allows to convert the string to object and access its property: "type", the type of data transmitted, "content", the content.

In our example, the client sends either a text (type text) or request an image (type image). Similarly, the server sends a text or the path of an image, that the client will then can display.

Server side code

var WebSocketServer = require("ws").Server;
var ws = new WebSocketServer( { port: 8100 } );

console.log("Server started...");

ws.on('connection', function (ws) {
  console.log("Browser connected online...")
   
  ws.on("message", function (str) {
     var ob = JSON.parse(str);
     switch(ob.type) {
     case 'text':
         console.log("Received: " + ob.content)
         ws.send('{ "type":"text", "content":"Server ready."}')
         break;
     case 'image':
         console.log("Received: " + ob.content)         
         console.log("Here is an apricot...")
         var path ="apricot.jpg";   
         var data = '{ "type":"image", "path":"' + path + '"}';
         ws.send(data); 
         break;
      }   
    })

    ws.on("close", function() {
        console.log("Browser gone.")
    })
});

The image path must be relative to the root of the application, such as "/subdir/image.png". The Chrome browser will refuse to assign to the src attribute of img a path on the local file system such as "c: /app/subdir/image.png".

Client side code

The page that is displayed sends a message to the server to confirm that the connection is established. The server responds with the message "Server ready.".

When the user clicks on the button "Send me a picture", the server receives a message of type "image". It then sends a message type "image", accompanied by the URL of the image.

The client displays the image in a canvas tag. Text messages are displayed in a layer with ID "message".

  var ws = new WebSocket("ws://localhost:8100");
  var canvas;
  var context;
  var image = new Image();

  window.onload=function() {
    canvas = document.getElementById("picture");
    context = canvas.getContext("2d");
  }

  function dispMessage(str) {
    document.getElementById("message").innerHTML = str;
  }

  ws.onopen = function (event) {
    ws.send('{ "type":"text", "content":"Browser ready."}' ); 
  };

  ws.onmessage=function(event) { 
   var message = JSON.parse(event.data);
   switch(message.type) {
     case "text":
        dispMessage(message.content);
        break;
     case "image":
        var iname = message.path;
        dispMessage("Received " + iname); 
        image.src= iname
        image.onload = function () { context.drawImage(image, 0, 0); }
        break;	
   }
 };

 function helloServer()
 {
    ws.send('{ "type": "image", "content":"Send me a picture"}'); 
 }
<form>
<input type="button" onclick="helloServer()" value="Send me a picture">
</form>
<fieldset><legend>Message</legend>
<div id="message"></div>
</fieldset>
<fieldset><legend>Picture</legend>
<canvas id="picture"></canvas>
</fieldset>

Latency issue

Sometimes, the browser is not ready yet to start to communicate with the server, so you receive this error message:

Uncaught InvalidStateError: 
Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.

To avoid this problem, replace the helloServer function by this code:

function helloServer()
 {
    setTimeout(function() {
        ws.send('{ "type": "image", "content":"Send me a picture"}'); 
    }, 50);
 }

This fifty milliseconds delay will be enough for the browser to reach the state "connected".

You may also wait for the connexion to be established before to start the program, with the onopen method:

socket.onopen = function (event) {      
  start();    
};

Server side in PHP

The PHP standard library does not support WebSocket (even if it support local socket communication), and we must also use a framework. Some example...

Whatever the framework used, the client-side code is the same as in the JavaScript example, but PHP side, it is necessary to convert a JSON string to object to access the content in key-value format.

However fortunately this time we have the necessary functions in the standard PHP library ...

json_decode converts a string containing a JSON object to a PHP object.

Example:

$json = '{ "type":"text", "content":"Some message..." }';
$phpobj = json_decode($json);

We can then access the property "type" or "content":

$type = $phpobj->{"type"};
$content = $phpobj->{"content"};

json_encode converts an associative array (equivalent to a JavaScript JSON object) to a string that can be sent to the client.

Example:

$path="apricot.jpg";
$phparr = array("type"=>"image", "path"=>$path);
$data = json_encode($phparr);

Our program will then look like this:

$json = $event->getMessage();
$phpobj = json_decode($json);
$type = $phpobj->{"type"};
switch($type) {
case 'text':
$content = $phpobj->{"content"};
echo "Received: $content\n";
$phparr = array("type"=>"text", "path"=>"Server ready.");
$data = json_encode($phparr);
$server.sendMessage($data);
break;
case 'image':
$content = $phpobj->{"content"};
echo "Received: $content\n";
$path="apricot.jpg";
$phparr = array("type"=>"image", "path"=>$path);
$data = json_encode($phparr);
$server.sendMessage($data);
break;
}

Everything else depends on the PHP framework that you have choosen, so please refer to the manual on the website of this framework.

Download

You can download the complete source code of a functional demonstration with JavaScript server-side.

The archive contains the following files:

© November 28, 2014 - Last updated April 13, 2016 - By Denis Sureau / Xul.fr