Le Web temps réel avec Socket.IO

Publié le 16 juillet 2013 par François Vaux | front

Cet article est publié sous licence CC BY-NC-SA

Le Web n’est plus une simple succession de pages liées entre elles via des liens hypertextes. De plus en plus, ce sont de véritables applications que l’on retrouve, proposant des degrés d’interactivité et de réactivité de plus en plus importants, et nécessitant une communication maximale entre serveur et client.

HTTP est un mode de transport unidirectionnel : le client envoie une requête, le serveur répond puis la connexion est fermée. Ceci pose évidement un problème si on veut continuellement pousser des données vers le client.

Nous allons montrer dans cet article comment Socket.IO propose une façon unifiée d’établir une communication bidirectionnelle entre client et serveur afin de s’affranchir des limitations des navigateurs.

Socket.IO

Socket.IO est une bibliothèque JavaScript utilisable côté serveur avec Node.js, ou côté client dans un navigateur.

Elle permet d’abstraire la communication temps réel en utilisant un mécanisme de transport permettant des connexions longues.

Si les Websockets sont disponibles dans le navigateur c’est elles qui seront choisies, sinon plusieurs autres techniques sont essayées (socket Flash, long polling AJAX, …).

L’API fournie est très simple à apprendre et à comprendre, et elle permet une grande souplesse dans le développement d’applications temps réel en JavaScript.

Installation

L’installation se fait simplement via npm, le gestionnaire de paquets de Node.js :

npm install socket.io

Vous pouvez utiliser l’option -g de npm pour l’installer au niveau global (auquel cas vous aurez sûrement besoin des droits d’administration).

Si vous installez en local, npm va créer un dossier node_module dans lequel il va stocker le contenu de la bibliothèque et de ses dépendances.

Premiers pas

Afin de voir la structure des messages échangés, on peut utiliser l’application de test suivante :

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

app.listen(3000);

function handler (req, res) {
  res.writeHead(200);
  res.end("<html><script src=\"/socket.io/socket.io.js\"></script><script></html>");
}

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

Celle-ci retransmet à tous les clients les messages qu’elle reçoit.

Pour la tester, enregistrez là dans un fichier broadcast.js, installez Socket.IO avec npm puis lancez là avec node broadcast.js.

Vous pouvez ensuite ouvrir un navigateur à l’adresse http://localhost:3000/ et ouvrir la console de ce dernier.

Comme vous pouvez le voir, Socket.IO sert automatiquement le fichier JS du client à l’URL /socket.io/socket.io.js. Ceci est très pratique car ça garantit qu’on utilise la bonne version du client par rapport à celle du serveur.

Pour le reste du code, on crée une application HTTP via la bibliothèque du même nom (livrée en standard avec Node.js), on l’attache au port 3000 et on attache notre application Socket.IO dessus (ligne 2).

Le handler se contente de servir le script.

Les 5 dernières lignes spécifient le comportement de l’application lorsqu’elle reçoit des messages via Socket.IO.

Ici, on attache sur toute socket établie un gestionnaire pour le message de nom message. Ce gestionnaire va simplement retransmettre le message à tout les autres clients.

Pour tester le comportement, utilisez la console du navigateur et tapez les lignes suivantes :

var socket = io.connect('http://localhost:3000')
socket.emit('message', {message: "Bonjour!"})

Vous verrez alors dans votre console que l’application écrit le contenu du message dans toutes les sockets exepcté l’initiatrice du message (vous pouvez mettre ce comportement en évidence en ouvrant plusieurs navigateurs).

Côté client, vous pouvez aussi attacher des gestionnaires. Dans la console du premier navigateur, exécutez :

socket.on('message', function(data) { console.log(data) });

Dans le deuxième navigateur, exécutez de nouveau l’appel à emit et vous verrez apparaître une ligne dans la console du premier.

Un peu plus loin

Afin d’explorer un peu plus les possibilités de Socket.IO, je vous recommande en premier lieu la lecture du How-To sur le site officiel, puis des pages du wiki qui détaillent certains points manquants du How-To.

Sans rentrer dans les détails, Socket.IO vous permettra aussi :

  • D’attacher des données aux sockets client dans le serveur, valables pour la durée de la session ;
  • D’utiliser un mécanisme de namespaces pour utiliser une seule connexion Socket.IO pour plusieurs composants. Ceci se révèle notamment pratique lorsqu’on emploie du code tiers en plus du sien ;
  • D’envoyer des messages volatiles (c’est à dire non garantis d’arriver à destination) via le serveur ;
  • De mettre en place un système d’accusés de réception sur les messages ;
  • De regrouper plusieurs sockets par salons (rooms) afin de faire du broadcast sur un groupe particulier de sockets.

Ce dernier point permet notamment de mettre en place extrêmement rapidement un système de messagerie de type pub/sub, non sans rappeler ce que propose Faye, une bibliothèque spécialisée dans ce mode de communication.

À votre tour !

Pour que vous puissiez expérimenter un peu plus en profondeur, j’ai mis à disposition le code d’une application permettant en plus du traditionnel chat, d’effectuer des jets de dés, comme pour simuler une table de jeu (de dés donc).

Ce code utilise notamment le principe de salons pour que les joueurs puissent se regrouper facilement sur différentes tables.

Vous retrouverez ce code sur le dépot synbioz/dicetable sur GitHub.