Mis à jour le 23/01/2018
  • 15 heures
  • Difficile

Socket.io: let’s go to real time!

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Socket.io is one of the most prized libraries by those who develop with Node.js. Why? Because it allows synchronized communication to take place simply within your app, which means real-time communication!

Can’t you see what this means? Let me say it another way: socket.io would allow you to set up a chat service on your website, for example! :)

The possibilities that socket.io offers are really immense and go far beyond chat. It’s useful for everything that needs immediate communication between the visitors to your website. This can be, for example, the first brick to build a game where we can see characters develop in the browser, all without needing to reload the page!

It’s tempting, isn’t it!

What does socket.io do?

Before starting to code, I would like to quickly explain the idea of socket.io. It’s a library that allows us to simplify a large number of things, but you’d be wrong to think it was ‘magic’. However, socket.io bases itself on a number of different techniques that allow real-time communication (and some of these have been around for years). The best-known and most recent one is WebSocket.

WebSocket? Isn’t that one of those HTML5 innovations?

It’s a recent development that appeared around the same time as HTML5, but it isn’t HTML: it’s a JavaScript API.

WebSocket is a feature supported by all recent browsers. It allows synchronized bilateral exchange between the client and the server.

What do you mean I’m speaking Double-Dutch?! Let’s go back to basics again. Communication on the web is usually unsynchronized. The Internet has always been this way: the client requests and the server responds (see next figure).

Communication is usually unsynchronized: the client requests, the server responds
Communication is usually unsynchronized: the client requests, the server responds

That was fine when the web was starting out, but it’s become too limiting in recent times. We need more reactive and immediate communication. In this diagram, for example, the server can’t decide for itself to send something to the client (for example to say: "oh there’s a new message!"). It has to be the client who reloads the page or takes action to call the server because it doesn’t have the right to talk to the client on its own.

WebSocket is an innovation that allows a sort of ‘tube’ of communication that remains open between the client and the server. The browser and the server stay connected to each other and can exchange messages, in one direction and the other, through this tube. So from now on the server can decide on its own to send a message to the client like a grown up (see next figure)!

With WebSocket, communication is synchronized: a communication tube stays open between the client and the server.
With WebSocket, communication is synchronized: a communication tube stays open between the client and the server.

Socket.io allows us to use WebSockets very easily. And, as all browsers don’t generate WebSockets, it is capable of using other synchronized communication techniques if they are managed by the client’s browser. Have a look at the Browser support section of the socket.io website. We can see that socket.io determines which real-time communication method is best suited to each client:

  • WebSocket

  • Adobe Flash Socket

  • AJAX long polling

  • AJAX multipart streaming

  • Forever Iframe

  • JSONP Polling

Thanks to all of these different techniques, socket.io supports a large number of browsers, even old ones:

  • Internet Explorer 5.5+ (yes, you read that correctly!).

  • Safari 3+

  • Google Chrome 4+

  • Firefox 3+

  • Opera 10.61+

  • Safari for iPhone and iPad

  • The Android browser

Now that we know a bit more about how socket.io works, could we start using it? :D

Sending and receiving messages with socket.io

Let’s get to the point: how do we use socket.io?

Installing socket.io

The first step, obvious as it may seem, is to install socket.io. Don’t laugh, the first time I wanted to use it I wasted 15 minutes before realizing that I had forgotten to do a simple:

npm install socket.io

I just saved you 15 minutes of your life! Don’t thank me, it’s only right and proper.

First code: a client logs on

When we use socket.io, we must always deal with two files at the same time:

  • The server file (e.g. app.js): it’s this one that centralizes and manages the connections of the different clients who are connected to the website.

  • The client file (e.g. index.html): it’s this one that connects to the server and displays results in the browser.

The server (app.js)

I deliberately separated the server code into two parts: at the beginning, we load the server as usual (and retrieve and return the content of the index.html page); then, we load socket.io and manage socket.io’s events.

var http = require('http');
var fs = require('fs');

// Loading the index file . html displayed to the client
var server = http.createServer(function(req, res) {
    fs.readFile('./index.html', 'utf-8', function(error, content) {
        res.writeHead(200, {"Content-Type": "text/html"});
        res.end(content);
    });
});

// Loading socket.io
var io = require('socket.io').listen(server);

// When a client connects, we note it in the console
io.sockets.on('connection', function (socket) {
    console.log('A client is connected!');
});


server.listen(8080);

This code does 2 things:

  • It sends back the index.html file when a client asks to change the page in their browser.

  • It prepares itself to receive requests via socket.io. Here, we are expecting to receive just one type of message: the connection. When we connect via soccer.io, we log the information in the console here.

Imagine you’re a visitor. You open your browser where the app is located (http://localhost:8080 in this case). We send you the index.html file, the page loads. In this file, which we are going to see in a minute, a JavaScript code connects to the server, this time not in http but via socket.io (therefore via WebSockets in general). The client carries out 2 types of connections:

  • A "classic" connection to the HTTP server to load the index.html page.

  • A "real time" connection to open a tunnel via the WebSockets thanks to socket.io.

The client (index.html)

Let’s look at the client for now. The index.html file is sent by the Node.js server. It’s an HTML file as classic as they come, the only difference being that it contains a bit of JavaScript that then allows for real-time communication with the server via socket.io:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Socket.io</title>
    </head>
 
    <body>
        <h1>Communication with socket.io!</h1>

        <script src="/socket.io/socket.io.js"></script>
        <script>
            var socket = io.connect('http://localhost:8080');
        </script>
    </body>
</html>

Firstly, In a fist instance, we retrieve the socket.io.js file from the client. This is automatically provided by the Node.js server via the socket.io module (so the path to the file isn’t chosen at random):

<script src="/socket.io/socket.io.js"></script>

The code that it contains enables management of communication with the server on the client side, either with WebSockets or with one of the other methods that I told you about if the browser doesn’t support them.

Then, we can carry out actions on the client side to communicate with the server. For the moment, I’ve done something very simple: I connected to the server. It’s on my machine, which explains the http://localhost:8080address. Obviously, on the web, the path will need to be adapted to show your website address (e.g.http://mysite.com).

var socket = io.connect('http://localhost:8080');
Let’s test the code!

You just need to launch the app:

node app.js

We can then go to our browser at this Node.js address: http://localhost:8080 here.

A basic page will load. Your computer will then open a connection with socket.io and the server should display debugging information in the console:

$ node app.js
   info  - socket.io started
   debug - client authorized
   info  - handshake authorized Z2E7aqIvOPPqv_XBn421
   debug - setting request GET /socket.io/1/websocket/Z2E7aqIvOPPqv_XBn421
   debug - set heartbeat interval for client Z2E7aqIvOPPqv_XBn421
   debug - client authorized for 
   debug - websocket writing 1::
A client is connected!

Great! This means that our code works. :)

For the moment it doesn’t do anything particularly extraordinary, but we’ve got the basics. Now it’s time to get down to exchanging messages with the server!

Sending and receiving messages

Now that the client is connected, we can exchange messages between the client and the server. There are 2 possible scenarios:

  • The server wants to send a message to the client.

  • The client wants to send a message to the server.

The server wants to send a message to the client.

I suggest that the server sends a message to the client when they connect, to confirm that the connection has worked properly. Add this to the app.s file:

io.sockets.on('connection', function (socket) {
        socket.emit('message', 'You are connected!');
});

When we detect a connection, we send a message to the client with socket.emit(). The function takes 2 settings:

  • The type of message that we want to transmit. Here, my message is message type (I’m not very creative, I know). This will allow you to distinguish between the different types of messages. For example, in a game, we could send "move_player", "attack_player" type messages.

  • The content of the message. Here you can say whatever you want.

On the index.html file side (the client), we’re going to listen to "message" type messages arriving:

<script>
    var socket = io.connect('http://localhost:8080');
    socket.on('message', function(message) {
        alert('The server has a message for you: ' + message);
    })
</script>

With socket.on(), we listen for message type messages. When the messages arrive, we call for the callback function that, in this case, displays a simple dialog box.

Try it. You’ll see that when you load the index.html page, a dialog box is displayed telling you that the connection was successful (see next figure).

The client displays the server’s message in a dialog box
The client displays the server’s message in a dialog box
The client wants to send a message to the server.

Now, let’s do it the other way around. I suggest adding a button in the web page that sends a message to the server when it’s clicked on.

On the client’s side (index.html), I’m going to add a "Poke the server" button. When we click on it, a message will be sent to the server. Here’s the complete code:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Socket.io</title>
    </head>
 
    <body>
        <h1>Communicating with socket .io!</h1>

        <p><input type="button" value="Poke the server" id="poke" /></p>


        <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
        <script src="/socket.io/socket.io.js"></script>
        <script>
            var socket = io.connect('http://localhost:8080');
            socket.on('message', function(message) {
                alert('The server has a message for you: ' + message);
            })

            $('#poke').click(function () {
                socket.emit('message', 'Hi server, how are you?');
            })
        </script>
    </body>
</html>

Finally, the only new and exciting code here is:

$('#poke').click(function () {
    socket.emit('message', 'Hi server, how are you?');
})

It’s very simple. When we click on the button we send a message type message to the server, along with content.

If we want to retrieve this on the server side, we are going to have to add listening to message type messages in the callback function of the connection:

io.sockets.on('connection', function (socket) {
    socket.emit('message', 'You are connected!');

    // When the server receives a “message” type signal from the client   
    socket.on('message', function (message) {
        console.log('A client is speaking to me! They’re saying: ' + message);
    }); 
});

Launch the code! Click on the Poke the server button on the page and watch the server console. You should see the next figure appear.

A client is talking to me! They’re saying: Hi server, how are you?
When a client clicks on the button, the server reacts immediately on the console
When a client clicks on the button, the server reacts immediately on the console

Communicating with several clients

In all our previous examples, we worked with one server and one client. In practice, you will probably have several clients connected to your Node.js application (or at least I hope you do!). To simulate this locally, it’s very simple: you just need to open two tabs, both on the http://localhost:8080 page. The server will see 2 different clients connected.

When we have several clients, we must be able to:

  • Send a message to everyone at the same time. We call those broadcasts.

  • Remember information about each client (like their username, for example). For that we need session variables.

Surprise surprise, that’s exactly what I was going to show you next!

Sending a message to all the clients (broadcasts)

When you do a socket.emit() on the server side, you only send a message to the client that you’re currently talking to. But you can do better than that: you can send a broadcast, meaning a message destined for all the other clients (except the one which the server just connected to).

Let’s take this scenario:

  1. Client A sends a message to the server.

  2. The server analyses it.

  3. It decides to broadcast this message and send it to the other clients who are connected: B and C.

In a broadcast, the server sends a message to all the other clients who are connected
In a broadcast, the server sends a message to all the other clients who are connected

Imagine for example a chat. Client A writes a message and sends it to the server. So that the other clients can see the message, it must be broadcast.

That’s really simple!

socket.broadcast.emit('message', 'Message to all units. I repeat, message to all units.');

You just need to do a socket.broadcast.emit() and the message will go to all other clients who are connected. Add a broadcast in app.js, for example, when a client connects:

io.sockets.on('connection', function (socket) {
    socket.emit('message', 'You are connected!');
    socket.broadcast.emit('message', 'Another client has just connected!');

    socket.on('message', function (message) {
        console.log('A client is speaking to me! They\'re saying: ' + message);
    }); 
});

Now try opening 2 tabs (or more) on your http://localhost:8080 page. You’ll see that when a new client arrives, the other pages react instantly and say: "Another client has just connected!"

Session variables

When you have several clients connected, you’ll soon realize that it is hard to recognize them. The ideal thing would be to be able to memorize information about each client in session variables... but session variables are not enabled by default by socket.io. 

Actually, session variables need to be managed in another library via a piece of middleware like session.socket.io (they work like the middleware in Express, a bit like plugins).

I would probably need to write a whole chapter to show you how to use a piece of middleware that manages sessions. To spare you that, I'm going to give you a magic trick: just save the information as a variable in the socket object of each client. It'll be easy to set up!

So we want to server to remember information about each client who's connected. This way, the client won't need to remind us who they are each time they send a message!

To stock a session variable on the server side, all you need to write is:

socket.myvariable = myvariable;

In this example, we store the data in a variable in the client's socket object (remember, the server stores one socket object for each client).

To retrieve this information in the future, you'll just need to see the contents of socket.myvariable:

console.log(socket.myvariable);

Simple, isn’t it? Let’s try to imagine a practical case. When a client connects, the webpage asks them for their username. The server will stock the username in the session variable to use it when the client clicks on "Poke the server".

Let’s see what modifications we need to do…

The web page (index.html) emits a signal containing the username

When the web page is loaded, we’re going to ask for the visitor’s username. We send this username to the server via a "little_newbie" signal (I called it this to differentiate it from the "message" signals). This signal contains the visitor’s username:

var username = prompt('What\'s your username?');
socket.emit('little_newbie', username);
The server (app.js) stores the username

The server must retrieve this signal. We then listen for the "little_newbie" signal and, when we receive it, we save the username as a session variable:

socket.on('little_newbie', function(username) {
    socket.username = username;
});
The server (app.js) remembers the username when we send it a message

Now, we would like the server to remember us when we poke it by clicking on the Poke server button (which triggers the sending of the "message" signal). We’re going to complete the callback function that is called when the server receives a "message":

socket.on('message', function (message) {
    console.log(socket.username + ' is speaking to me! They\'re saying: ' + message);
});

As soon as we receive a message, we ask for the username session variable to be retrieved from the client's socket.

Testing the code

Try to open 2 windows giving different usernames each time. Then click on Poke the server. You’ll see the username of the person who clicked on the button in the console!

I did the test for myself with two windows, one with the username "mateo21" and the other with the username "robert". In the next figure, you can see at the bottom of the console that it has actually recognized who just clicked!

Several clients connected: the server remembers their names!
Several clients connected: the server remembers their names!
The complete code

I deliberatley gave you very short bits of code to explain the idea to you, but I’m sure you’re dying to have the full code to carry out your tests. ;)

So, let’s go! Here’s index.html:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Socket.io</title>
    </head>
 
    <body>
        <h1>Communicating with socket.io!</h1>

        <p><input type="button" value="Poke the server" id="poke" /></p>


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

            // The visitor is asked for their username...
            var username = prompt('What\'s your username?');
            
            // It's sent with the signal "little_newbie" (to differentiate it from "message")
            socket.emit('little_newbie', username);

            // A dialog box is displayed when the server sends us a "message"
            socket.on('message', function(message) {
                alert('The server has a message for you: ' + message);
            })

            // When the button is clicked, a "message" is sent to the server
            $('#poke').click(function () {
                socket.emit('message', 'Hi server, how are you?');
            })
        </script>
    </body>
</html>

… and here is the app.'s application server:

var http = require('http');
var fs = require('fs');

// Loading the file index.html displayed to the client
var server = http.createServer(function(req, res) {
    fs.readFile('./index.html', 'utf-8', function(error, content) {
        res.writeHead(200, {"Content-Type": "text/html"});
        res.end(content);
    });
});

// Loading socket.io
var io = require('socket.io').listen(server);

io.sockets.on('connection', function (socket, username) {
    // When the client connects, they are sent a message
    socket.emit('message', 'You are connected!');
    // The other clients are told that someone new has arrived
    socket.broadcast.emit('message', 'Another client has just connected!');

    // As soon as the username is received, it's stored as a session variable
    socket.on('little_newbie', function(username) {
        socket.username = username;
    });

    // When a "message" is received (click on the button), it's logged in the console
    socket.on('message', function (message) {
        // The username of the person who clicked is retrieved from the session variables
        console.log(socket.username + ' is speaking to me! They\'re saying: ' + message);
    }); 
});


server.listen(8080);

I hope that I have explained commented the code well enough so you can find your way round it. ;)

Summing up

  • Socket.io is a Node.js module that allows your visitors to communicate continuously (in real time) with the server when the page is loaded.

  • Socket.io is based on WebSockets, a sort of ‘super AJAX’. Your app can call the server at any time without reloading the page and the opposite is also true: your visitors can receive messages from the server at any time!

  • The server and the client send each other events (with socket.emit() ) and listen to the events that are sent (with socket.on() ).

Exemple de certificat de réussite
Exemple de certificat de réussite