Introduction to the HTML5 WebSockets API

Sandeep Panda

HTML5 has introduced many new interesting APIs, including WebSockets. WebSockets allow developers to create powerful real time applications by establishing socket connections between the browser and the server. In other words both the client and server can exchange data at any time because of a persistent connection. This tutorial explains how to use WebSockets to create real time web applications.

The Problem

In real time applications, the connection between server and client must be persistent. So, in order to create an illusion of server initiated transfer long polling is usually used. WebSockets solve this issue by establishing a persistent socket connection between the client and server. Once the connection is established, it remains open until the client or server wants to close it. It significantly reduces the burden on the server and is best suited for low latency applications.

Getting Started

Opening a WebSocket connection is fairly simple. You just need to call the WebSocket() constructor to create a connection.

var connection=new WebSocket("ws://localhost:8787",['soap','json']);

ws: and wss: are the URL schemas for normal and secured WebSocket connections, respectively. The second parameter is used to define the sub protocol name which can be an array of strings or a string. However, the server will accept only one sub protocol. During the lifetime of the connection the browser will receive several events such as connection opened, message received and connection closed. To handle these events use the following code:

var connection=new WebSocket("ws://localhost:8787",'json');
connection.onopen = function () {
  connection.send('Hello, Server!!'); //send a message to server once connection is opened.
};
connection.onerror = function (error) {
  console.log('Error Logged: ' + error); //log errors
};
connection.onmessage = function (e) {
  console.log('Received From Server: ' + e.data); //log the received message
};

As soon as the connection is opened, the browser sends a message to the server using connection.send(). If an error is encountered, the above code simply logs it. If at any time the server sends a message to the browser, the onmessage callback is fired. The event handler gets an event object, with the data property of the object containing the received message.

The connection.send() method can be used to send binary data as well. To do so you can use either a Blob or an ArrayBuffer. The following code demonstrates using an ArrayBuffer to send an image drawn on canvas to server.

var image = canvas2DContext.getImageData(0, 0, 440, 300);
var binary_data = new Uint8Array(image.data.length);
for (var i = 0; i < image.data.length; i++) {
  binary_data[i] = image.data[i];
}
connection.send(binary_data.buffer);

Similarly, the received message can be a string or binary data. The binary data can be received as a blob or an arraybuffer.

Simple WebSocket Application

In order to a create a working application, you also need a server side implementation. It’s possible to use technologies like node.js, Java, .NET, Ruby, or C++ to create server side implementation. This section will show you how to create a simple application using WebSockets.

The sample application will allow the user to ask specific questions to server. The server side implementation is done using the Java jWebSocket framework on Windows 7. So, to get the environment set up follow these simple steps. I assume you have already installed the latest JDK (JDK 7)on your Windows 7 PC.

Step 1

Head over to jWebSocket Downloads and download the first zip which is marked as server.

Step 2

Unzip the archive, and place it somewhere in your C:. Then, create a new environment variable named JWEBSOCKET_HOME, which references the root of your jWebSocket installation. This is the path to the jWebSocket-1.0 folder. Add the following JARs to your class path:

  • JWEBSOCKET_HOME/libs/jWebSocketServer-1.0.jar
  • JWEBSOCKET_HOME/libs/jWebSocketServerAPI-1.0.jar
  • JWEBSOCKET_HOME/libs/jWebSocketCommon-1.0.jar

Step 3

Create a new Java Source file and name it SocketListener.java. Add the following code to this file.

import java.util.Date;
import java.text.SimpleDateFormat;
import org.jwebsocket.factory.JWebSocketFactory;
import org.jwebsocket.server.TokenServer;
import org.jwebsocket.kit.WebSocketServerEvent;
import org.jwebsocket.api.WebSocketServerListener;
import org.jwebsocket.api.WebSocketPacket;
import org.jwebsocket.config.JWebSocketConfig;
import org.jwebsocket.instance.JWebSocketInstance;
class JWebSocketListener implements WebSocketServerListener {
  public void processOpened(WebSocketServerEvent event) {
    System.out.println("Connection Opened");
  }
  public void processPacket(WebSocketServerEvent event, WebSocketPacket packet) {
    switch(packet.getString()){
      case "1":
        packet.setString("My Name is jWebSocketServer");
        break;
      case "2":
        packet.setString("Windows 7 64 Bit");
        break;
      case "3":
        SimpleDateFormat sdf=new SimpleDateFormat("hh:mm:ss");
        packet.setString(sdf.format(new Date()));
        break;
    }
    event.sendPacket(packet);
  }
  public void processClosed(WebSocketServerEvent event) {
  }
}
public class SocketListener{
  public static void main(String[] args){
    JWebSocketFactory.printCopyrightToConsole();
    JWebSocketConfig.initForConsoleApp(new String[]{});
    JWebSocketFactory.start();
    TokenServer server = (TokenServer)JWebSocketFactory.getServer("ts0");
    if(server!=null) {
      server.addListener(new JWebSocketListener());
    }
    while (JWebSocketInstance.getStatus() != JWebSocketInstance.SHUTTING_DOWN){
      try {
        Thread.sleep(250);
      }
      catch (InterruptedException e) {
      }
    }
  }
}

Explanation

The code implements the WebSocketServerListener interface. The interface declares the following three methods which should be implemented in our class, JWebSocketListener.

  • processOpened()
  • processPacket()
  • processClosed()

processOpened() is called once a connection is opened. An example use of this will be starting a thread that sends updates to client in regular intervals. Similarly, processClosed() is called when the connection closes so that you can perform any clean ups.

In our application, the major processing is done in processPacket(). This method is called whenever the browser sends a message to the server. It receives two objects, of types WebSocketServerEvent and WebSocketPacket. First, we use WebSocketPacket#getString() to read the message (which is a question asked by the client). Depending on the question the server sends an answer. The message to be sent is wrapped inside a WebSocketPacket using WebSocketPacket#setString(). Then, we call WebSocketServerEvent#sendPacket(), passing the packet as an argument. Next, we create a public class called SocketListener. This class starts a WebSocket implementation server and registers our custom listener to it.

Step 4

Compile SocketListener.java, and start your server using the command java SocketListener.

Step 5

Now that you have done the server side implementation, it’s time to create the client that will interact with the server. The following is our initial HTML markup:

<html>
  <head>
    <title>WebSocket Test</title>
    <style>
      #response{
        width: 200px;
        background: #F2F2F2;
        padding: 120px 0px 120px 0px;
        font-size:18px;
      }
    </style>
  </head>
  <body>
    <div align="center">
      Choose a question to ask:
      <select id="question" name="question" onchange="sendMessage(this.value);">
        <option value="1">What's Your Name</option>
        <option value="2">Which OS You Are Running On?</option>
        <option value="3">What Time Is It?</option>
      </select>
      <br/><br/>
      <div id="response">
        My Name is jWebSocketServer
      </div>
    </div>
  </body>
</html>

Now, add the following JavaScript to the HTML:

<script type="text/javascript">
var connection = new WebSocket('ws://localhost:8787', 'json');
connection.onopen = function () {
  console.log('Connection Opened');
};
connection.onerror = function (error) {
  console.log('WebSocket Error ' + error);
};
connection.onmessage = function (e) {
  if(e.data.indexOf("subProtocol")==-1)
    document.getElementById("response").innerHTML=e.data+"<br/>";
};
function sendMessage(msg){
  connection.send(msg);
}
</script>

Explanation

We created an HTML file that allows users to choose questions from a drop down menu. When an onchange event fires, we take the value of the selected item and send that value to the server. The server then processes the request and sends back an answer to the browser. When the message from the server is received, the onmessage callback is executed, which shows the response in the response <div>. The line if(e.data.indexOf("subProtocol")==-1) is not mandatory. I have included it because initially when the connection is opened, the server sends a long string containing information to the browser. As we don’t want to show this string, I have included the above condition.

Note: Don’t directly open this HTML file. Put it in the localhost server and then access it in the browser.

Conclusion

Using the WebSocket API, you can create very powerful real time applications. But, keep in mind that cross origin communication is allowed by WebSockets. So, you should only communicate with servers and clients that you trust. The following are a few example apps that you can create with this API:

  • Realtime Social Stream Updating
  • HTML5 Multiplayer Games
  • Online Chat Applications

Check out Mozilla Developer Network to learn more about the WebSockets API.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Abhisek P.

    You showed here how to do server side implementation using Java. How do I do a C++ implementation?

    • http://gadgeticworld.com/ Sandeep Panda

      To do so you need a C++ WebSocket implementation library. Take a look at this listing which shows different C++ implementations available. You can use any of them.