LOGO Lionel Groulx

Site Web du cours « Programmation de Systèmes »

Node.js Chapitre 6
Utiliser socket.io pour le temps-réel client-serveur :

 

La conception d’une application ayant des exigences « temps-réel », et basée sur un site web client-serveur http avec Node.js va exiger que l’on établisse des liaisons événementielles depuis le serveur vers la ou les pages clientes, et également des liaisons événementielles depuis le ou les pages clientes vers le serveur.

 

1/ Du serveur au(x) client(s) : Imaginons une application où le serveur Web serait hébergé par un module de type « Raspberry », directement en liaison avec des capteurs. Sur un changement de valeur rapporté par un capteur, le module Raspberry devra « pousser » l’information vers chaque client en train de consulter sa page Web.

Plusieurs techniques sont envisageables pour réaliser ceci :

-  La première (que l’on pourrait appeler technique du « Polling ») consiste à provoquer un « rafraîchissement » cyclique de la page (total ou même partiel) sur le client, qui périodiquement relancerait un GET auprès du serveur. Cette solution est de loin la moins élégante pour plusieurs raisons : d’abord la notion pure de « temps réel » n’est pas vraiment respectée, car c’est la fréquence de rafraîchissement de la page qui donnerait la précision temporelle de l’information affichée. Ensuite, la lourdeur de la page peut provoquer un très lourd trafic sur le réseau, surtout si plusieurs clients réclament un rafraîchissement cyclique. Enfin, on comprend que la grosse majorité de ce trafic sera inchangé, donc inutile de le recharger, même si ce problème peut être amoindri par un rechargement partiel de la page. Cette technique est détaillée dans les chapitres 4 et 5 du Référentiel JQuery.

-  La seconde consiste à lancer un événement du côté serveur, qui provoquera la mise à jour ciblée de la variable à changer dans la/les page(s) client(s). Cette technique est de loin préférable car elle ne surcharge pas le trafic sur le réseau, garantit une meilleure adéquation avec la notion de temps réel, et évite des rechargements lourds et pas toujours pertinents dans le navigateur. En revanche, elle nécessite de s’appuyer sur un protocole d’événement qui va partir du serveur, et se répandre chez tous les clients (« broadcast »), en ciblant quelle variable a changé, et en précisant sa nouvelle valeur.

 

2/ Dans l’autre sens (d’un des clients vers le serveur), on peut imaginer des actionneurs connectés au Raspberry, attendant un ordre d’allumage ou d’extinction (une ampoule par exemple). Si l’on imagine que l’utilisateur affiche la page Web « client » sur son téléphone cellulaire et que celui-ci actionne un bouton sur cette page Web afin d’allumer (ou éteindre) l’ampoule à distance par exemple, il faut mettre en place un événement qui sera envoyé de la page au serveur afin que le module Raspberry allume l’ampoule. Plusieurs techniques sont ici aussi envisageables :

-  Comme vu précédemment, on pourrait mettre en place un formulaire par GET ou par POST (voir Référentiel HTML, Référentiel PHP et ci-dessus chapitre 4.1.2), qui fournirait au serveur l’information d’action. Cependant la structure d’un formulaire en HTML est assez figée, et nécessite classiquement un « bouton de validation ». Ainsi si l’on voulait dessiner la commande de notre actionneur dans la page client autrement (comme par exemple un curseur horizontal), ce serait plus compliqué avec cette technique.

-  Une alternative serait là encore d’exploiter un mécanisme événementiel, pas forcément lié à un formulaire, qui nous laisserait plus libre de notre choix esthétique dans la page (utilisation de JavaScript client, JQuery par exemple).

 

Dans les 2 cas précités, la solution la plus optimale se révèle être Socket.io.

Socket.io est une bibliothèque JavaScript pour les applications Web en temps réel. Il permet une communication bidirectionnelle en temps réel entre les clients Web et le serveur. Il comprend deux parties: une bibliothèque côté client qui s'exécute dans le navigateur et une bibliothèque côté serveur pour Node.js.

Socket.io s’appuie (entre autres) sur la technologie des « WebSockets » : c’est justement ceci qui permet un échange temps-réel et dans les 2 sens entre les clients et le serveur. Les WebSockets sont actuellement supportés par tous les principaux navigateurs du marché. C’est une nouvelle connexion qui s’établit entre eux, en « parallèle » du http.

C’est très utilisé par les applications de « chat » (clavardage) temps-réel.

6.1.   Installation de Socket.io

Installer Socket.io dans notre projet Node.js est très simple :

npm install socket.io

Ne pas oublier d’ajouter l’option « -save » si le projet est déjà initié avec un « package.json ».

6.2.   Socket.io du serveur vers le(s) client(s)

En premier lieu, 2 choses sont à faire : initialiser la connexion dans le serveur, et dans la page client. Ensuite, le serveur peut envoyer un message, et le client peut « capter » ce message.

6.2.1.      Pour initialiser la connexion dans le serveur

Vous devez initialement avoir une ligne dans votre code Node.js qui ressemble à :

var server = app.listen(8080);

Rajoutez-y en-dessous l’activation de Socket.io :

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

Voilà, maintenant l’objet « io » vous fournira un événement du type « connection » :

io.sockets.on('connection', function(socket) {

    console.log('Un client est connecté!');

});

6.2.2.      Pour initialiser la connexion dans la page client

Dans votre page HTML (ou EJS) rajoutez ceci à la fin du body :

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

<script>

    var socket = io.connect('http://localhost:8080');

</script>

6.2.3.      Pour envoyer un message avec le serveur

Il suffit de compléter à l’intérieur de l’événement créé ci-dessus :

io.sockets.on('connection', function (socket) {

    socket.emit('message', 'Vous êtes bien connecté !');

});

Ici le mot-clé « message » n’est pas obligatoire, vous pourriez mettre « toto ». Cependant de l’autre côté, pour construire un événement qui pourra recevoir le message, il faudra spécifier le même mot-clé. Ainsi, on peut différentier nos « canaux » de communication.

Dans cet exemple, le message est envoyé uniquement au client qui vient de se connecter, comme conséquence à sa connexion.

Si l’on veut envoyer un message À TOUS LES AUTRES CLIENTS, on utilise le « broadcast » :

io.sockets.on('connection', function (socket) {

    socket.broadcast.emit('message', 'Un autre client vient de se connecter!');

});

6.2.4.      Pour recevoir le message dans la page client

On rajoute l’événement « socket.on » entre nos balises <script> :

<script>

    var socket = io.connect('http://localhost:8080');

    socket.on('message', function(message) {

        alert('Message du serveur : ' + message);

    });

</script>

 

Ici nous avons décidé que la conséquence de l’événement serait une fenêtre « pop-up » avec la commande « alert », mais on peut exploiter JQuery pour faire ce qu’on veut : modifier le texte d’une balise, ou autre.

6.3.   Socket.io d’un client vers le serveur

6.3.1.      Pour envoyer un message depuis la page vers le serveur

Pour ce faire, il est plus facile de s’appuyer sur JQuery (Voir Référentiel JQuery).

Admettons que notre page HTML possède un bouton, que l’on va identifier avec « id="poke" » :

<input type="button" value="Embêter le serveur" id="poke" />

On va émettre un signal en conséquence du clic sur ce bouton en utilisant JQuery. Entre les balises <script> de la page, rajouter ceci :

$('#poke').click(function () {

     socket.emit('message', 'Salut serveur, ça va ?');

});

Ici encore le mot-clé « message » est au choix, afin d’identifier le type de message, et de construire l’événement associé dans le serveur pour le capter.

6.3.2.      Pour recevoir le message dans le serveur

À l’intérieur de l’événement de création de connexion vu précédemment (« io.sockets.on »), rajouter ceci :

io.sockets.on('connection', function (socket) {

    socket.on('message', function(message) {

          console.log('message du client:'+message);

    });

});

Si l’on veut qu’un message émis d’un client soit reçu par tous les clients, il faut qu’il soit envoyé spécifiquement d’abord au serveur, et que celui-ci s’occupe du « broadcast » vers les autres clients.

Pour aller plus loin avec Socket.io, je vous conseille de consulter les liens dans la médiagraphie Node.js.