Web Sockets is a new addition to HTML5 that allows you to create a persistent connection with a server for communication. With Ajax, because you’re using HTTP to communicate, the connection between your client and the server must be reestablished each time you want to get or send data. This is fine if your application doesn’t need to get or send data a lot, but if you’ve built an application that, for example, gets regular updates of data (e.g. a real-time sports leader board, a stock ticker, a chat room, or a MMOG) then the overhead of creating those connections each and every time can make your app sluggish. Web Sockets can vastly improve the efficiency of communication in these types of apps. Note that as of this post, Web Sockets are supported only in Chrome and Safari.

One of the trickiest things about getting started with Web Sockets is getting a server set up. Because you’re not using HTTP, you can’t just use your regular web server. Two implementations of Web Socket severs are socket.io and Jetty, but the set up for these is not obvious, particularly if you’re a client-side person and don’t have much experience with the server-side of things.

socket.io

Socket.io is a Node.js implementation of the Web Socket protocol. That means to use it, you also need Node.js. Fortunately, Node.js is a lot easier to install than it was just a short while ago. You’ll also need npm (Node Package Manager), which makes installing extensions to node.js, like socket.io, much easier. You’ll use npm to install socket.io. Here are the steps and downloads you’ll need:

  1. Download and install node.js. This one-click install should work on most platforms.
  2. Next, get and install npm. The npm web page has a “one line install” that works; except you need to be running as root/administrator to do it. (Mac users: You can also try using sudo on both the curl and sh commands, but that didn’t work for me). If you’re on a Mac, to login as root, you’ll first need to create a root user and then login as root to do the install. Follow these instructions (apple.com) to enable and login as root. Once you’re logged in as root, you can run the “one line” at npm to do the npm install. Logout of root, back in as your normal user and you can then use npm to install socket.io, in the next step. If you’re on Windows, you’ll have to do your own digging as I’m not up to date on admin stuff on Windows.
  3. To install socket.io, you’ll need to download it from github. Look for the ZIP link on that page. Unzip to a folder in the directory where you’re going to run everything, and use npm to install it (from the level above the unzipped folder). To install, type: npm install socket.io. You’ll see a new folder, node_modules created in the directory, containing socket.io in a configuration that node.js can find it with a require.

Now that you’ve got everything installed, you’re ready to give it a try. You’ll need to create two files: the server file, that you’ll run with node.js, and a client HTML file that you’ll load in the browser. One tricky thing about socket.io (that took me a while to figure out!) is that the HTML file must be served using the Web Socket server to use the socket.io client library. I tried writing basic HTML5 Web Socket code, but I kept running into a bug, and apparently it’s a known and common bug, so the recommendation is that you use the socket.io client library to connect to the server. Unfortunately this hides the HTML5 Web Socket communications code, which is what you really want to learn, but that’s the current state of things with socket.io (from my understanding, anyway, and certainly my experience was that I ran into the bug).

Server code

Create a new file, ss.js and add the server code:

var app = require('http').createServer(handler)
  , io = require('socket.io').listen(app)
  , fs = require('fs')

app.listen(8080);

function handler (req, res) {
  fs.readFile(__dirname + '/index.html',
  function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }

    res.writeHead(200);
    res.end(data);
  });
}

io.sockets.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

This code is copied directly from the socket.io web site (first example), although I did change the port to 8080, as I already have an HTTP server running on port 80. First, we create an HTTP server. The callback function for the server (that is, the function that gets called when a connection is requested) is the handler function. This serves an index.html file, which is where you’ll put the client code. This is just a very basic http file server, so if you want a more fully functional web server, you’ll need to use Express, and there are instructions on how to use that on the socket.io web site. I’m not going to go into that in this article.

After setting up the http server, we then set up the Web Socket handler. The Web Socket connections will come in on the same port as the HTTP requests, 8080, but use a different protocol (ws rather than http). When a ws connection is received, a socket is created, and we send a message to the client over that socket. The message consists of the event type and an object with one property, hello.

We also set up a message handler for the socket. When a message is received from the client, we just log it to the console. Due to lack of documentation at socket.io, I’m a little unclear on the first argument to socket.emit and socket.on but I believe it’s the event type. You can use built-in event types, like “connection”, or create custom event types, like “my other event”.

Client code

Now for the client. Create a file, index.html (remember our basic server is only going to serve index.html! Feel free to rename the file but you’ll need to change the name in the server code too). Here’s my code (slightly modified from the code in the example on socket.io):

<!doctype html>
<html>
<head>
<title>web sockets</title>
<meta charset="utf-8">
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost:8080');
socket.on('news', function (data) {
    console.log(data);
    writeMessage(data);
    socket.emit('my other event', { my: 'data' });
});

function writeMessage(msg) {
    var msgArea = document.getElementById("msgArea");
    if (typeof msg == "object") {
        msgArea.innerHTML = msg.hello;
    }
    else {
        msgArea.innerHTML = msg;
    }
}
</script>
</head>
<body>
<div id="msgArea">
</div>
</body>
</html>

Notice that we’re using the same port as we are listening on in the server, 8080. These two must match! Also notice that we’re including the socket.io JavaScript from the path: /socket.io/socket.io.js. The / at the beginning means “root”. The server is running in the directory where you’re putting these files, and that’s considered the “root”. The socket.io library is installed in this same directory for node.js, so this path will be found when you request this file using localhost:8080, which points to your node server. The node server does some magic that allows all the code in the socket.io.js file to be understood by the browser. If you try to load this file without socket.io and node, it will fail miserably. This was a tricky bit for me to understand so make sure you’ve got everything in the right place!

So, to try this, you first need to run the node server, and then load up your index.html file in the browser:

  1. To run the node server, type node ss.js. You should see:
    info  - socket.io started
    
  2. Now, use your browser to load http://localhost:8080. If all goes well, it will connect to the server and you’ll see “world” in your page, and in the console, where you’re running the server, you’ll see:
       debug - served static content /socket.io.js
       debug - client authorized
       info  - handshake authorized 1292119758764894899
       debug - setting request GET 
               /socket.io/1/websocket/1292119758764894899
       debug - set heartbeat interval for client 
               1292119758764894899
       debug - client authorized for 
       debug - websocket writing 1::
       debug - websocket writing 5:::
               {"name":"news","args":[{"hello":"world"}]}
       debug - websocket received data packet 5:::
               {"name":"my other event","args":[{"my":"data"}]}
               { my: 'data' }
    

And voila, you have just used Web Sockets to send and receive a message from a server! Whew!

Jetty

Another option, and one that allows you to write HTML5 Web Socket code on the client, rather than using a framework, is to use a Jetty Web Socket server. Jetty is a Java HTTP server and servlet container, and has an implementation of Web Sockets, and a Web Socket test server. I’ll explain how to get the Jetty Web Socket test server up and running, and then how to create a client that uses HTML5 Web Sockets to send and receive messages.

First, you’ll need to download Jetty. You can download the entire Jetty folder here: http://download.eclipse.org/jetty/. This includes all the jar files you’ll need except one; also download the jetty-all jar file, which includes everything; this makes it simpler to compile and run:


http://repo1.maven.org/maven2/org/eclipse/jetty/aggregate/
jetty-all/$JETTY_VERSION/jetty-all-$JETTY_VERSION.jar

where $JETTY_VERSION corresponds to the version of jetty you downloaded previously. The version I downloaded was: 8.1.0.v20120127, so my url for downloading the jetty-all file was:


http://repo1.maven.org/maven2/org/eclipse/jetty/aggregate/
jetty-all/8.1.0.v20120127/jetty-all-8.1.0.v20120127.jar

On the Mac, you can use curl to get this file easily:

curl http://repo1.maven.org/maven2/org/eclipse/jetty/aggregate/
jetty-all/8.1.0.v20120127/jetty-all-8.1.0.v20120127.jar
> jetty-all-8.1.0.v20120127.jar

Create a folder, and place jetty-all-8.1.0.v20120127.jar as well as servlet-api-3.0.jar (you’ll find that in the full Jetty distribution you downloaded previously) in that folder.

The server

On the Jetty Web Sockets page, the documentation says you can run the test server directly. I wasn’t able to do this (I get an error). So I copied the server code and built it myself. The code seems to be missing the following import:

import org.eclipse.jetty.websocket.*;

so I added that before compiling. You’ll need to compile with the jetty-all-8.1.0.v20120127.jar and servlet-api-3.0.jar. I did this using Eclipse, so to run the Test Server, I exported a jar file, and placed it in the same directory with jetty-all-8.1.0.v20120127.jar and servlet-api-3.0.jar. If you want to do it from the command line, you can do this:


javac -cp jetty-all-8.1.0.v20120127.jar:servlet-api-3.0.jar
TestServer.java
jar -cf TestServer.jar *.class

Once you’ve got the Test Server built, you can run it like this:


java -cp .:TestServer.jar:servlet-api-3.0.jar:
jetty-all-8.1.0.v20120127.jar TestServer
--port 8080 --docroot . --verbose

You’ve now got a Web Socket server running at port 8080.

If you take a look at the code, you’ll notice that there are several different “protocols” supported. The protocol is actually a sub-protocol of the Web Socket protocol and allows the server to further refine the types of connections it will accept.

The Jetty Web Socket server supports several different sub-protocols, and the one I found most useful when testing is the “org.ietf.websocket.test-echo-assemble” sub-protocol. This one tells the Jetty server to send a message back, echoing the data you sent from the client. This is a good way to test to make sure your code is working. You can specify which sub-protocol to use in the client code, when you connect.

The client

Now that you have a server running, it’s time to create the client. I based my code on code in this NetTut tutorial, but changed it a bit to suit my needs and make it a bit clearer.

Here’s the code:

<!doctype html>
<html>
<head>
<title>Node Test</title>
<meta charset="utf-8">
<script>
window.onload = init;

function message(message) {
  var msg = document.getElementById("msg");
  msg.innerHTML += message;
}

function init() {
  var socket;  
  if (!("WebSocket" in window)){  
    message("Sorry, no web sockets");
  }
  else {
    //The user has WebSockets  
    setUpHandlers();
    connect();  
  
    function connect(){  
      var host = "ws://localhost:8080/";
      var protocol = "org.ietf.websocket.test-echo-assemble"; 

      try {  
          socket = new WebSocket(host, protocol);  
          message('<p class="event">Socket Status: '
                   +socket.readyState + "</p>");  
          socket.onopen = function() {  
             message('<p class="event">Socket Status: '
                     +socket.readyState+' (open)</p>');  
          }  
          socket.onmessage = function(msg) {  
             message('<p class="message">Received: '
                     + msg.data + "</p>");  
          }  
          socket.onclose = function() {  
             message('<p class="event">Socket Status: '+
                      socket.readyState+' (Closed)</p>');  
          }           
       } catch(exception) {  
          message('<p>Error'+exception + "</p>");  
       }  
    }//End connect  

    function send() {  
      var text = document.getElementById("text").value;
      if (text=="") {  
        message('<p class="warning">Please enter a message</p>');  
        return ;  
      }  
      try {  
        socket.send(text);  
        message('<p class="event">Sent: '+text+"</p>");
      } catch(exception) {  
        message('<p class="warning">' + exception.text + "</p>");  
      }  
    }//End send

    function setUpHandlers() {
      var sendButton = document.getElementById("send");
      sendButton.onclick = function() {
        send();
      };
      var disconnectButton = document.getElementById("disconnect");
      disconnectButton.onclick = function() {
        socket.close();
      };
    }//End setUpHandlers
  }//End else  
} //End init
</script>
</head>
<body>
  <div id="msg">
  </div>
  <form>
    <input id="text" size="40">
    <input type="button" id="send" value="Send">
    <input type="button" id="disconnect" value="Disconnect">
  </form>
</body>
</html>

In this code, we use a form to get a message from the user to send to the server, which the server will echo back.

We set up the connection to the server with the line:


socket = new WebSocket(host, protocol);

Note that the host points to the local Jetty server which is running on port 8080. The protocol is “org.ietf.websocket.test-echo-assemble”, which tells the server to echo back the message you send to it.

We set up handlers for various socket events and states. We use onopen to set up a callback when the socket is opened successfully, and onclose to set up a callback when the socket is closed. If a message is received, we handle it with the callback set up for onmessage.

Try typing a word or two in the form, and click Send. This calls the send() function, which sends a message to the server using socket.send(). The server echoes that message back to the client, so you should see the words you typed appear in the page. You can keep sending messages to (and receiving messages from) the server (all on the same socket connection), until you click Disconnect, which terminates that socket connection.

Web Sockets are not for the faint of heart, for sure! In this post, we’ve looked at how to get a Web Socket server up and running and create a very basic client to connect to the server, send messages to the server, and receive messages from the server. In a future post, we’ll do something more fun with Web Sockets.

We wrote the book: with over a 1,000,000 books sold, we’ve created some of the industries top-selling software & technology titles. We teach the experts: you find us teaching on today’s premier training sites, like Lynda.com and Safaribook’s Online, and we’ve created digital experiences for a small, but exceptional group of industry leaders and luminaries. Throughout it all, we’ve applied our wickedly smart philosophy to get you there faster with amazing results. How can we help you?

We wrote the book: with over a 1,000,000 books sold, we’ve created some of the industries top-selling software & technology titles. We teach the experts: you find us teaching on today’s premier training sites, like Lynda.com and Safaribook’s Online, and we’ve created digital experiences for a small, but exceptional group of industry leaders and luminaries. Throughout it all, we’ve applied our wickedly smart philosophy to get you there faster with amazing results. How can we help you?

Don't miss out!

Don't miss out on brain-friendly WickedlySmart updates early access to books and general cool stuff! Just give us your email and we'll send you something about once a week. 

You have Successfully Subscribed!