הלינקייה: מגזין חודשי למפתחים

רוצה לשמוע על כל האירועים, המדריכים, הקורסים והמאמרים שנכתבו החודש ?
הלינקייה הינו מגזין חופשי בעברית שמשאיר אותך בעניינים.
בלי ספאם. בלי שטויות. פעם בחודש אצלך בתיבה.

Realtime Web with SocketIO

HTTP Protocol is based on a request/response model, in which the client sends a request and the server sends back the response. This can pose a problem in cases where the server has something new to tell a client.
Picture a chat application mediated by the server. Alice sends a request and asks the server to send a message to Bob. Unfortunately, until Bob calls the server next time to check his messages, we're unable to deliver the message to him.

Having a bidirectional socket between the server and its clients is an effective way for the server to notify clients in realtime on changes. HTML5 already has native browser support for this using a protocol named Web Sockets.

Web Sockets are not perfect though, they're not supported on old browsers, and even on the new browsers developers need to write boilerplate code that handles keepalive packets and lost connections.

Meet Socket.IO

Socket.IO is a project by Guillermo Rauch at Learnboost Labs. It's released under the MIT license and provides all the magic you need to create working web sockets for all browsers.

Socket.IO has both server and client side libraries, will fallback on old browsers and provide all the boilerplate code you need to write a modern client-server realtime applications.

Today many projects use Socket.IO for small and large projects. You can see a partial list at LearnBoost website.

Let's Start With An Echo Server

To give you a feel of Socket.IO, I'd like to start by writing an echo server. On the server side we'll use Node.JS and Express. Here's the server code:

var e    = require('express');
var sio  = require('socket.io');
var http = require('http');

var app = e();
var server = http.createServer( app );

var io  = sio.listen(server);

io.sockets.on('connection', function(socket) {
  socket.on('ping', function(msg) {
    socket.emit('pong', msg );
  });
});

app.get('/', function(req, res) {
  res.sendfile('sockets.html');
});

server.listen(3000);

Socket.IO entry point is io.sockets.on, this method takes an event name and a callback, and attaches the callback to the event.
In the above example, connection event is triggered when a new client socket connects.

For further functionality, we take the socket and binds more event handlers on it. We can only do that inside the connection handler (when we actually have a socket).

In the above example, a ping event is a user-defined event that is triggered on the socket by the client, when that happens, we respond with a pong event preserving the sent data.

Here's the client code (file sockets.html):

<!DOCTYPE html>
<html>
<head>
    <title>Sockets Demo</title>
</head>
<body>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        var socket = io.connect('http://localhost');

        socket.on('pong', function(msg) {
            console.dir( msg );
        });

        socket.on('connect', function() {
            socket.emit('ping', {msg: 'Hello'});
        });
    </script>
</body>
</html>

The client creates a socket and tries to connect with io.connect. We can already bind new event handlers on this socket, so the two next blocks handle the connect event and pong event. As you remember, pong event is a custom event triggered by the server.

Note we send ping event after a connection was established. Both ping and pong event names are made up. You can use any name you want as long as you're consistent between client and server.

Turn That Into A Public Chat Room

The difference between an echo server and a public chat room is actually not that large. To make our echo server public, all we need is for socket.io to broadcast the message instead of returning it.

Instead of using socket.emit to send the message, our public server uses io.sockets.emit. The command sends the message to all connected clients. I also changed the event names to be more aligned with a chat server.

It's also possible to use socket.broadcast.emit, which tells the socket to send a message to all clients except the one who sent it.

Here's the updated server code:

var e    = require('express');
var sio  = require('socket.io');
var http = require('http');

var app = e();
var server = http.createServer( app );

var io  = sio.listen(server);

io.sockets.on('connection', function(socket) {
  socket.on('new message', function(msg) {
    io.sockets.emit('new message', msg );
  });
});

app.get('/', function(req, res) {
  res.sendfile('sockets.html');
});

server.listen(3000);

What about the client ? Well, our client needs a bit more work. First, we'll need to handle incoming messages differently (by adding them to the DOM). Second, we need to have an input where a user can type her own message.

Using jQuery, here's the resulting client code:

<!DOCTYPE html>
<html>
<head>
    <title>Sockets Demo</title>
</head>
<body>
    <ul></ul>

    <form>
        <input type="text" />
    </form>

    <script src="http://code.jquery.com/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        var socket = io.connect('http://localhost');

        socket.on('new message', function(msg) {
            console.dir( msg );
            $('ul').append('<li>' +  msg.text + '</li>')
        });

        $('form').on('submit', function() {
            var text = $('input').val();
            $('input').val('');

            socket.emit('new message', { text: text });
            return false;
        });
    </script>
</body>
</html>

Where To Go Next

Socket.IO has a lot to offer to developers. If you find the technology interesting, I highly recommend you check the documentation on LearnBoost Github account. Here are the awesome links:

  1. Introduction and Cookbook
  2. Authorization
  3. Short Socket.IO Video