Home About Me

A Practical Walkthrough for HTML5 WebSockets

WebSockets are a push technology built for two-way communication. They are moving toward W3C standardization, and recent Chrome and Safari releases already support them.

The problem they address is familiar: long polling. In a long-polling setup, the client sends a request, and the server does not immediately answer. It holds the connection open until a timeout is reached or new data is ready. The client receives that response, then opens another request. This can reduce latency, since an open connection avoids some of the cost of starting from scratch, but it is not elegant. A request can stall while waiting, forcing another connection, and many Ajax applications using this pattern can waste server resources because connections stay tied up.

A better model would let the server wake up and send data to the client that needs it without the same reserved-connection overhead. That is the push model WebSockets provides.

Get a WebSocket server running

This walkthrough is mostly about the client, but the client needs something to talk to. The example environment uses XAMPP on Windows 7 to provide PHP, along with phpwebsockets, a WebSocket server written in PHP. Depending on the build, phpwebsockets may need small fixes. Other implementations are available, so if one does not work, try another and continue with the same client steps.

  • jWebSocket (Java)
  • web-socket-ruby (Ruby)
  • Socket IO-node (Node.js)

Start Apache in XAMPP before continuing.

Apache service running

Adjust the server URL and interface

The server configuration may need changes during setup. For example, in setup.class.php:

public function __construct($host='localhost',$port=8000,$max=100)
{
    $this->createSocket($host,$port);
}

Open the relevant files and change the host, port, or other values where appropriate.

Build the client page

Start with a basic template. The sample client.php looks like this:

<!DOCTYPE html>
<html>
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

    <title>WebSockets Client</title>

</head>
<body>
    <div id="wrapper">

    <div id="container">

    <h1>WebSockets Client</h1>

    <div id="chatLog">

    </div><!-- #chatLog -->
    <p id="examples">e.g. try 'hi', 'name', 'age', 'today'</p>

    <input id="text" type="text" />
    <button id="disconnect">Disconnect</button>

    </div><!-- #container -->

    </div>
</body>
</html>

Add some CSS

Nothing elaborate is required; the goal is to space the elements out enough to work with them.

body {
font-family:Arial, Helvetica, sans-serif;
}
#container{
border:5px solid grey;
width:800px;
margin:0 auto;
padding:10px;
}
#chatLog{
padding:5px;
border:1px solid black;
}
#chatLog p {
margin:0;
}
.event {
color:#999;
}
.warning{
font-weight:bold;
color:#CCC;
}

Understand the WebSocket events

Before wiring up the page, it helps to know which events matter.

WebSocket event flow

There are three events to use:

  • onopen: a socket connection is opened
  • onmessage: a message is received
  • onclose: a socket connection is closed

What can be done with them?

First, create a WebSocket object:

var socket = new WebSocket("ws://localhost:8000/socket/server/startDaemon.php");

Checking whether an event fires is straightforward:

socket.onopen = function(){
alert("Socket has been opened!");
}

Receiving a message works the same way:

socket.onmessage = function(msg){
alert(msg); //Awesome!
}

In a real page, avoid relying on alert boxes and put the information into the client interface instead.

Write the JavaScript

Place the code inside $(document).ready(function() {}), then check whether the browser supports WebSockets. If it does not, fade out the chat controls and append a link pointing to Chrome.

$(document).ready(function() {
if(!("WebSocket" in window)){
$('#chatLog, input, button, #examples').fadeOut("fast");
$('<p>Oh no, you need a browser that supports WebSockets. How about <a href="http://www.google.com/chrome">Google Chrome</a>?</p>').appendTo('#container');
}else{

//The user has WebSockets

connect();

function connect(){
//the connect function code is below

}
});

If the browser supports WebSockets, execution continues into a connect() function. That function brings together the open, message, and close events.

First define the server URL:

var socket;
var host = "ws://localhost:8000/socket/server/startDaemon.php";

Notice that the URL does not use http. It uses the WebSocket scheme, which is different from an HTTP URL. The address breaks down like this:

WebSocket URL structure

Inside connect(), wrap the work in try/catch so failures can be reported to the user. Create a new WebSocket connection, pass messages through a message function, and define onopen, onmessage, and onclose. Showing the socket state is not required, but it helps with debugging.

  • CONNECTING = 0
  • OPEN = 1
  • CLOSED = 2
  function connect(){
  try{

  var socket;
  var host = "ws://localhost:8000/socket/server/startDaemon.php";
  var socket = new WebSocket(host);

  message('<p class="event">Socket Status: '+socket.readyState);

  socket.onopen = function(){
   message('<p class="event">Socket Status: '+socket.readyState+' (open)');
  }

  socket.onmessage = function(msg){
   message('<p class="message">Received: '+msg.data);
  }

  socket.onclose = function(){
   message('<p class="event">Socket Status: '+socket.readyState+' (Closed)');
  }

  } catch(exception){
   message('<p>Error'+exception);
  }
  }

The message function is simple: append the supplied text to chatLog. The socket event handlers include the opening paragraph tag and class, so the function only has to close the paragraph.

function message(msg){
$('#chatLog').append(msg+'</p>');
}

At this point the page can create a WebSocket connection and keep the user informed about its progress.

WebSocket client page

Send data

Rather than requiring a button click, listen for the Enter key and call send. The ASCII value for Enter is 13.

$('#text').keypress(function(event) {
if (event.keyCode == '13') {
send();
}
});

Here is the send() function:

function send(){

var text = $('#text').val();
if(text==""){
message('<p class="warning">Please enter a message');
return ;
}
try{
socket.send(text);
message('<p class="event">Sent: '+text)
} catch(exception){
message('<p class="warning"> Error:' + exception);
}

$('#text').val("");

}

The underlying call is just socket.send(), despite the surrounding UI checks.

Close the socket

Closing the socket takes a click handler on the disconnect button.

Disconnect button

$('#disconnect').click(function(){
socket.close();
});

The complete JavaScript

$(document).ready(function() {

  if(!("WebSocket" in window)){
  $('#chatLog, input, button, #examples').fadeOut("fast");
  $('<p>Oh no, you need a browser that supports WebSockets. How about <a href="http://www.google.com/chrome">Google Chrome</a>?</p>').appendTo('#container');
  }else{
  //The user has WebSockets

  connect();

  function connect(){
  var socket;
  var host = "ws://localhost:8000/socket/server/startDaemon.php";

  try{
  var socket = new WebSocket(host);

  message('<p class="event">Socket Status: '+socket.readyState);

  socket.onopen = function(){
 message('<p class="event">Socket Status: '+socket.readyState+' (open)');
  }

  socket.onmessage = function(msg){
 message('<p class="message">Received: '+msg.data);
  }

  socket.onclose = function(){
message('<p class="event">Socket Status: '+socket.readyState+' (Closed)');
  }

  } catch(exception){
 message('<p>Error'+exception);
  }

  function send(){
  var text = $('#text').val();

  if(text==""){
  message('<p class="warning">Please enter a message');
  return ;
  }
  try{
  socket.send(text);
  message('<p class="event">Sent: '+text)

  } catch(exception){
 message('<p class="warning">');
  }
  $('#text').val("");
  }

  function message(msg){
$('#chatLog').append(msg+'</p>');
  }

  $('#text').keypress(function(event) {
  if (event.keyCode == '13') {
send();
  }
  });

  $('#disconnect').click(function(){
 socket.close();
  });

  }//End connect

  }//End else

});

Start the WebSocket server

The server has to be started from the command line. XAMPP includes a shell option for this. Open the XAMPP control panel, click Shell, and run:

php -q path\to\server.php

XAMPP shell

The WebSocket server is now running.

Try it

When the page loads, it attempts to establish a WebSocket connection. The code can be changed so that the user decides when to connect and disconnect. Once connected, the user can enter messages and receive responses from the server.

Chat example

WebSockets are useful, but the API was still evolving when this pattern was current. Keep an eye on the W3C WebSocket API for changes.