Introduction

Image non disponible
Socket.IO

Pour ce Quick Start, je vous propose une application qui fera du push sur tous les clients pour leur envoyer l'heure système ainsi que le nombre de clients actuellement connectés au serveur (avec gestion de la déconnexion). Ces clients pourront envoyer des pokes aux autres clients, le tout tournera avec express et Socket.IO.

L'idée n'est pas de développer une application de gestion complète (qui mériterait une bonne dizaine de billets) ni de faire un tour d'horizon de la centaine de bibliothèques existantes, mais juste de vous montrer à quoi ressemble le code d'une application Node avec deux bibliothèques très répandues dans la communauté.

Installation

Normalement, vous devriez avoir Node et NPM installés sur votre machine (Débuter avec Node.js partie 1 § installation). Pour rappel, selon votre OS et votre package manager si besoin (ici Mac OSX et Homebrew) :

 
Sélectionnez

rmat0n:~$ brew install node
rmat0n:~$ curl http://npmjs.org/install.sh | sudo sh

Dès lors, vous n'avez plus que deux commandes à effectuer pour installer les modules express et Socket.IO pour Node :

 
Sélectionnez

rmat0n:~$ npm install express
rmat0n:~$ npm install socket.io

Vous voilà prêt, commençons tout de suite !

Arborescence

Le projet aura l'arborescence suivante :

 
Sélectionnez

/ project
    / public
        index.html
    server.js

Très simple donc avec un fichier server.js et dans un dossier public, un fichier index.html.

Client

Tout d'abord, voyons le squelette du fichier HTML :

 
Sélectionnez

<html>
<head>
  <title>Socket IO Demo</title>
  <script src="http://code.jquery.com/jquery-1.5.min.js"></script>
  <script src="/socket.io/socket.io.js"></script>
</head>
<body>
  <div>
    <h2>Démo Node.js + Socket IO</h2>
    <label for="timestamp">Timestamp</label>
    <div id="timestamp"></div>
    <label for="clients">Clients</label>
    <div id="clients"></div>
    <label for="message">Message</label>
    <div id="message"></div>
    <p><button id="poke">Send poke !</button></p>
  </div>
</body>
</html>

Un fichier très standard donc, avec un import du dernier jQuery, l'import de Socket.IO (module chargé par Node) et quelques div qui me permettront d'afficher les retours du serveur.

À la suite des deux balises script déjà présentes, je rajoute le code JavaScript suivant :

 
Sélectionnez

<script type="text/javascript">
    $(document).ready(function () {
      var sock = new io.Socket();
      sock.on('message', function (data) {
        var obj = JSON.parse(data);
        if(obj.message) {
          $('#message').text(obj.message);
        } else {
          $('#timestamp').text(obj.timestamp);
          $('#clients').text(obj.clients);
        }
      });
      sock.connect();
      $("#poke").click(function() { sock.send("Poke !"); });
    });
  </script>

Quelques explications :

  • tout d'abord, je crée un object io.Socket (qui par défaut utilisera le host d'entrée et le port 80) ;
  • lorsque le serveur m'envoie un message, je peux le récupérer côté client en utilisant le code suivant (API Socket.IO) : sock.on('message', function(data) {...}); ;
  • le serveur me renverra un objet JSON, je le parcours et récupère les propriétés message ou timestamp et client que je mets dans les champs associés ;
  • je connecte la socket immédiatement après ;
  • je définis un gestionnaire d'événement sur le bouton et lors d'un clic utilisateur, j'envoie un message poke.

Plutôt simple non ? Même si l'API standard est déjà très simple d'utilisation, Socket.IO nous simplifie encore plus l'utilisation de Web Sockets côté client et côté serveur.

Voyons maintenant le code côté serveur.

Serveur

Et voici le fichier server.js :

 
Sélectionnez

var io = require('socket.io');
var express = require('express');
 
var app = express.createServer();
app.configure(function(){
  app.use(express.static(__dirname + '/public'));
});
app.get('/', function(req, res, next){
  res.render('./public/index.html');
});
app.listen(8333);
 
var socket = io.listen(app, {
  flashPolicyServer: false,
  transports: ['websocket', 'flashsocket', 'htmlfile', 'xhr-multipart', 'xhr-polling', 'jsonp-polling']
});
var allClients = 0;
var clientId = 1;
socket.on('connection', function(client) {
  var my_timer;
  var my_client = { "id": clientId, "obj": client };
  clientId += 1;
  allClients += 1;
  my_timer = setInterval(function () {
    my_client.obj.send(JSON.stringify({ "timestamp": (new Date()).getTime(), "clients": allClients }));
  }, 1000);
  client.on('message', function(data) {
    my_client.obj.broadcast(JSON.stringify({ message: "poke send by client "+my_client.id }));
    console.log(data);
  });
  client.on('disconnect', function() {
    clearTimeout(my_timer);
    allClients -= 1;
    console.log('disconnect');
  });
});

Dans le détail :

  • j'importe les modules Socket.IO et express ;
  • je crée le serveur express, je configure le dossier public en statique, je mappe l'URL racine '/' vers le fichier index.html et je démarre le serveur ; on voit ici que express nous simplifie grandement le mapping entre une URL et une action (mais il n'est pas le seul) avec une bonne gestion des paramètres, j'aime beaucoup cette bibliothèque car elle me rappelle le très puissant Spring MVC ;
  • je crée ensuite l'objet socket en spécifiant quelques transports et en supprimant le flash policy server (inutile pour notre exemple) ;
  • lorsqu'un client se connecte, je le stocke dans un objet JSON, j'augmente la variable id et j'augmente le nombre de clients connectés (variable allClients) ;
  • je lance un setInterval pour envoyer toutes les secondes l'heure courante du serveur ainsi que le nombre de clients connectés (le JSON qui sera récupéré dans mon client, cf. code du § client) ;
  • si un client envoie un message côté serveur, je le répercute à tous les autres avec un message générique mentionnant l'id du client qui a envoyé le message ;
  • enfin, sur une déconnexion d'un client, je stoppe le setInterval lié à ce client et je décrémente la variable allClients ; celle-ci sera ensuite envoyée à tous les autres clients lors d'un prochain envoi de données du setInterval.

Il y a bien sûr différentes manières de faire ce code, celui-ci ne sert qu'à vous montrer ce que l'on peut faire :

  • envoyer un message à un client spécifique ;
  • renvoyer un message d'un client vers tous les autres ;
  • mettre à jour des variables globales utilisées par tous les clients ;

D'ailleurs, si vous avez d'autres astuces concernant les Web Sockets dans Node, n'hésitez pas à les partager en commentaires !

Run baby !

Bon, il n'y a plus qu'à lancer le code et à vous rendre à l'URL http://localhost:8333/ :

 
Sélectionnez

rmat0n:~/dev/node-example$ node server.js

Et maintenant, amusez-vous à lancer plusieurs navigateurs à cette URL, à envoyer des pokes, à vous déconnecter…

Image non disponible
Image non disponible

Conclusion et remerciements

Bien sûr, on est très loin d'une application de gestion et ce n'est pas le but de cet article. Mais vous pouvez voir avec cet exemple un code très minimal qui fait des Web Sockets, du send, du broadcast, du MVC… le tout très facilement !

Prochaine étape et dernier article sur Node : déployer ce projet sur un serveur Node et pour cela nous allons regarder de plus près l'offre de Nodester.

Pour ceux qui veulent récupérer le projet, le code se trouve ici : node-example (ou télécharger l'archive).

Cet article a été publié avec l'aimable autorisation de Web Tambouille, l'article original (Node.JS partie 3 - Première application Node.js et HTML5) peut être vu sur Web Tambouille.

Nous tenons à remercier ClaudeLELOUP et _Max_ pour leur relecture attentive de cet article.