I'm Morgan McGuire (@CasualEffects). I've been working on computer graphics and games for 20 years at great places including NVIDIA, University of Waterloo, Williams College, Brown University, Roblox, Unity, and Activision.

See my home page for a full index of my blog posts, books, research, and projects.

Tuesday, August 7, 2012

Getting started with Socket.IO

Socket.IO is a library for Javascript (and ported to other languages) that provides client-server network connections. Under the hood it uses WebSockets, Flash, ActiveX, and other tools to provide an implementation that works on many browsers. It is a great tool, but the documentation is poor. So here's some information about how to get started that is missing from the manual.

Socket.IO is divided into two pieces.  The first is a package that runs on the server under Node.js, the server-side JavaScript interpreter.  The second is the client script that is included into your web page.  The client script can trigger downloading of additional downloads.  See my previous post on installing Node.js and Socket.IO for information on how to install the necessary packages on your server. In particular, you must have already run

   sudo npm install socket.io

on the server in the directory that your server-side app.js will reside in.  If you're running a Unix-based operating system (i.e., not Windows) and don't plan to check your app into revision control, you can do a global install of Socket.IO and set up a symlink instead. I prefer a platform-independent approach for my own work.

Your First Socket.IO Program

The first demo in the Socket.IO documentation is a minimalist program that demonstrates client-server communication. The server piece is:

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

app.listen(80);

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);
  });
});
The client piece is:

index.html

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io.connect('http://localhost');
  socket.on('news', function (data) {
    console.log(data);
    socket.emit('my other event', { my: 'data' });
  });
</script>

The current version of the documentation doesn't tell you how to actually run this, however. One piece of crucial information is that "index.html" is hard-coded into the server, so your client code must be stored in a file with that name, and it must be in the same directory as app.js on the server.

The demo is also hardcoded to use port 80. This means that you must open that port on your firewall. On OS X, ports with values number 1024 may only be listened on by a program launched by a superuser, regardless of the firewall settings.  This means that you must run the program by going (in Terminal) to the directory that contains index.html and app.js and typing:

    sudo node app.js

Next, open a web browser.  In your web browser, open the JavaScript console since the demo doesn't display anything in the HTML page itself.  Load the URL:

    http://localhost/index.html

You should see one Object printed in the JavaScript console in your browser (client).  The object will be {hello: "world"}:




The Terminal (your server), should display something that looks like:


~/Projects/socketexample$ sudo node relay.js
Password:
   info  - socket.io started
   debug - served static content /socket.io.js
   debug - client authorized
   info  - handshake authorized GumjgLEbQtioj1dxMPvx
   debug - setting request GET /socket.io/1/websocket/GumjgLEbQtioj1dxMPvx
   debug - set heartbeat interval for client GumjgLEbQtioj1dxMPvx
   debug - client authorized for 
   debug - websocket writing 1::
   debug - websocket writing 5:::{"name":"news","args:[{"hello":"world"}]}
{ my: 'data' }

If you forget to run node using sudo, then you will see the access error:

   info  - socket.io started
   warn  - error raised: Error: listen EACCES


Serving the Client

This demo makes the server into both a (very simple) web server and a custom "hello world" server. It is important that it is a web server because that is how the demo serves the Socket.IO client library to the browser.  That is, the "/socket.io/socket.io.js" is implicitly transmitted using Node.js's http module.  If you do not want your server program to also be a web server that is responsible for serving the web page and the Socket.IO client, then you need to copy the client files to your web server.  These are stored (relative to your current project) in:

   node_modules/socket.io/node_modules/socket.io-client/dist/

You should then be able to change the path in index.html to point to a copy of the script running on your regular web server.  This might be the same machine as your program server, but it allows you to use your web server program for serving files and let your application only contain application code. It potentially reduces the security risks--supporting fewer protocols means fewer opportunities for bugs.

Running a Server in a Browser

It is not possible to run a server program in a browser with Socket.IO.  That is, the client API cannot open a listener socket. So you have to be running the program that accepts incoming connections on a physical server machine from the command line.

My current workaround is to build a relay server using Node.js that a browser-based "server" program can register with. The relay server then simply repeats messages that it receives to this server and sends the responses back to the clients.  Note that this doubles the latency in the typical case, and requires you to run the relay server somewhere.

I wrote an open source relay server for Node.js called relay.js.  You can find relay.js and a demo of a browser-based client and server (clientserver/index.html)  that use it on my open source code page.


Separating the Web Server and Application Server

See my next post for detailed information on accessing the web server in OS X Mountain Lion and on writing a Node.js + Socket.IO program that works with a regular web server.


Morgan McGuire is a professor of Computer Science at Williams College and a professional game developer. He is the author of The Graphics Codex, an essential reference for computer graphics that runs on iPhone, iPad, and iPod Touch.