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…
Great tutorial, thanks!
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!
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!