Getting started with Socket.IO

There's no shortage of blog posts which — like this one — provide an introduction to Socket.IO. Many, though, were written prior to the release of 0.7, which ushered in significant API changes. Here I'll provide examples of server- and client-side code using APIs provided by the current version (0.7.4 at time of writing).

The code snippets are in CoffeeScript and as such are largely free of the parentheses, squiggly brackets, and semicolons that riddle the equivalent JavaScript code. For those unfamiliar with CoffeeScript's syntax, here's the fifteen second rundown:

qux = foo 'bar', (baz) -> 'Hello, world!'

The above is equivalent to:

var qux = foo('bar', function (baz) {
  return 'Hello, world!';
});

CoffeeScript uses -> rather than the function keyword, and parentheses are optional for most function invocations. Now, let's get started.

Step 1: Create a server

This is Node 101 stuff:

fs   = require 'fs'
http = require 'http'

server = http.createServer (req, res) ->
  fs.readFile "#{__dirname}/socket.io.demo.html", (err, data) ->
    res.writeHead 200, 'Content-Type': 'text/html'
    res.end data, 'utf8'

server.listen 1337

The server we've created simply responds to any request on port 1337 with the contents of "socket.io.demo.html", which must reside in the same directory as the script we're creating.

Step 2: Add server-side event handlers

io = require('socket.io').listen server

io.sockets.on 'connection', (socket) ->

  socket.on 'publish', (message) ->
    io.sockets.send message

  socket.on 'broadcast', (message) ->
    socket.broadcast.send message

  socket.on 'whisper', (message) ->
    socket.broadcast.emit 'secret', message

Here we've instructed our server to listen for three custom events: "publish", "broadcast", and "whisper". Note that these particular names are not special in any way.

When the server receives one of these events, it invokes the appropriate handler with the event's data as the sole argument. Since we're expecting a string argument in each of these cases, we've named the parameter message.

The "publish" handler passes message to io.sockets.send, which forwards it to all the clients. The "broadcast" handler invokes socket.broadcast.send, which forwards message to all the clients except the one that emitted the "broadcast" event. The "whisper" handler is very different in that it doesn't send a message at all. Rather, it emits yet another event. Again, the name of this event — "secret" — is not special in any way.

Having completed these two steps our server-side code is done, so we could now run coffee -c -b socket.io.demo.coffee to produce the actual JavaScript file we'll run in Node. We're now ready to tackle the client-side component.

Step 3: Create the HTML file

<!doctype html>
<html>
  <head>
    <title>Socket.IO demo</title>
  </head>
  <body>
    <h1>Socket.IO demo</h1>
    <input type="text" autofocus="autofocus" />
    <button type="button">publish</button>
    <button type="button">broadcast</button>
    <button type="button">whisper</button>
    <p>Status: <span id="status">Undefined</span></p>
    <ol id="messages"></ol>
    <script src="/socket.io/socket.io.js"></script>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script src="http://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script>
    <script type="text/coffeescript">

      jQuery ($) ->

        // TODO: add client-side logic

    </script>
  </body>
</html>

<script src="/socket.io/socket.io.js"></script> was the line that most confused me in the tutorials I read. I assumed that I'd need to serve this file myself, which turned out not to be the case. Somehow, it just works.

Note the inclusion of "coffee-script.js", which enables us to write our client-side logic in CoffeeScript, too. :)

Step 4: Add client-side Socket.IO event handlers

$status = $ '#status'
socket = io.connect()

socket.on 'connect', ->
  $status.text 'Connected'

socket.on 'disconnect', ->
  $status.text 'Disconnected'

socket.on 'reconnecting', (seconds) ->
  $status.text "Reconnecting in #{seconds} seconds"

socket.on 'reconnect', ->
  $status.text 'Reconnected'

socket.on 'reconnect_failed', ->
  $status.text 'Failed to reconnect'

socket.on 'message', (message) ->
  $('<li>').text(message).appendTo $('#messages')

socket.on 'secret', (message) ->
  console.log message

The first five events — "connect", "disconnect", "reconnecting", "reconnect", and "reconnect_failed" — are emitted by Socket.IO in response to changes in the connection status. We've registered a handler for each in order to expose this information to users.

We've also added handlers for "message" and "secret" events. Our "message" handler will be called whenever the server receives a "publish" or "broadcast" event (io.sockets.send and socket.broadcast.send emit "message" events). Our "secret" handler will be called whenever the server receives a "whisper" event.

All that remains is to have the client emit appropriate custom events in response to input from users.

Step 5: Add DOM event handlers which emit custom events

$input = $ 'input'

$('button').click ->
  socket.emit $(this).text(), $input.val()
  $input.val('').focus()

Here, we've bound a "click" handler to the three buttons. Whenever one of these buttons is clicked the appropriate event is emitted ("publish", "broadcast", or "whisper" depending on the button clicked). The current value of the text input is used as the event's data.

That's it. We can now run node socket.io.demo.js to start our server and confirm that things behave as expected.

How it all fits together

Let's look at exactly what happens when a user types "the password is 1234" into the text field and clicks "whisper".

First, our "click" handler captures the DOM event and in turn emits our custom "whisper" event with "the password is 1234" as its data.

Next, the server — which is listening for our custom events — captures the "whisper" event. It then emits a "secret" event to all clients except the one that sent the "whisper", passing along the event data.

Finally, each client — bar the originator of this chain of events — captures the "secret" event and logs "the password is 1234" to the console.

Code

The code used in this tutorial is available on Bitbucket.

Comments

was the line that most confused me me too

I had a previously installed socket.io (client) and it was just 1 js file. I checked the socket.io repo as well as socket.io-client and i did not find this dir hierarchy.

However my node is fresh (0.7.8) and running smoothly…

Tom

Great tutorial, thanks!

Grant Keiser

Thanks, Grant. I'm pleased to hear you found it useful.

I, too, didnt understand the script include and thought I had to serve it up myself. Did you figure out why that works? Thanks for the tut, and love the design on your blog!

Frank

I tracked down the code behind the "magic". It's in manager.js:

/**
 * Dictionary for static file serving
 *
 * @api public
 */

Manager.static = {
    cache: {}
  , paths: {
      ...
      , '/socket.io.js':  client.dist + '/socket.io.js'
      ...
    }
    ...

It feels good to understand!

Respond