Comprendre les sockets avec Socket.io

Qu’est qu’un socket?

Un socket est une interface de communication entre deux noeuds dans un réseau informatique. Plus concrètement, entre deux ordinateurs, et pour les développeurs web entre un serveur et un client, aka votre ordinateur et plus précisément le navigateur.

Côté serveur

On va pour simplifier utiliser Nodejs, avec un script en local on va pouvoir en quelques ligne mettre en oeuvre un socket grâce à la librairie socket.io

Côté client

Grâce à une librairie socket.io-client, on va pouvoir se connecter à un socket.

Quelle est la différence entre un socket et une requête AJAX?

Dans le monde du HTTP, une communication entre client et serveur ne dure qu’un instant, entre deux communications il n’y a rien. Vous faites une requête AJAX, le client initie une demande (Request), le serveur réponse avec un Response, et puis c’est fini.

Dans le cas d’un socket, une fois que le client initie une demande de connexion, la communication s’établie et dure tant que l’on n’a pas arrêté, elle ne s’arrête pas toute seule. Le socket sert à maintenir une connexion persistente, avec un flot continu de données de part et d’autre.

Le socket répond au besoin de recevoir contnuellement des données, une application comme une courbe de bourse en temps réel a besoin d’un socket, cela permet une mise à jour temps réel du cours de bourse. Bien sû on pourrait faire toute les secondes une requête AJAX, mais c’est très consommateur de données, de par la nature du protocole HTTP. Socket ne fait pas partie de la technologie HTTP.

Démarrer un projet avec socket.io

Créez un nouveau répertoire de projet, ouvrez le avec VSCode, ouvrez un terminal sous VSCode.

Créer un fichier package.json comme ci-dessous:

{
  "name": "app-socket",
  "version": "0.1",
  "description": "mon socket.io app",
  "dependencies": {}
}

Installez Express

Express est un framework pour NodeJS en MVC.

npm install express@4

Créez le fichier index.js

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);

app.get('/', (req, res) => {
  res.send('<h1>Hello world</h1>');
});

server.listen(3000, () => {
  console.log('listening on *:3000');
});

vous pouvez lancer le serveur NodeJS avec la commande:

node index.js

ça va initialiser un serveur qui écoute sur le port 3000 (par défaut pour NodeJS). Si dans votre navigateur, vous ouvrez l’url http://localhost:3000. Gardez un oeil sur le terminal de VSCode également.

Ajout d’un fichier HTML

Nous allons améliorer notre code avec une page HTML à servir

<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }

      #form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed; bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box; backdrop-filter: blur(10px); }
      #input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin: 0.25rem; }
      #input:focus { outline: none; }
      #form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem; border-radius: 3px; outline: none; color: #fff; }

      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages > li { padding: 0.5rem 1rem; }
      #messages > li:nth-child(odd) { background: #efefef; }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form id="form" action="">
      <input id="input" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>

Changez le code d’index.js

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

Relancez votre serveur avec node index.js

Note : Pour éviter de redémarrer à la main à chaque fois qu’il y a une mise à jour du code, je vous suggère d’installer nodemon, un démon NodeJS, qui va redémarrer le serveur automatiquement à chaque enregistrement de fichier.

npm install --save-dev nodemon  // installe en tant que dépendance de développement
// redémarrer le serveur
nodemon ./index.js localhost 3000

Intégration de socket.io

Maintenant on va allez voir les choses intéressantes, l’intégration de socket !

Il nous faut agir sur deux fronts, sur le serveur et sur le client. Il y a donc deux partie à la librairie socket.io. (nous sommes toujours en architecture client/serveur)

Socket.io côté serveur

npm install socket.io

et éditons le fichier index.js

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

// début de code de gestion du socket
io.on('connection', (socket) => {
  console.log('a user connected');
  socket.on('disconnect', () => {
    console.log('user disconnected');
  });
});

// fin de code de gestion du socket

server.listen(3000, () => {
  console.log('listening on *:3000');
});

Notez qu’on crée le serveur de socket en lui passant dans le constructeur le server NodeJS. « connection » est l’événement qui se déclenche lorsque la page dans le front end se charge pour la première fois et qu’un message donc est envoyé au serveur.

Socket.io côté client

Ajoutez le snippet dans le fichier HTML avant la fermeture de body

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();
</script>

ça y est notre application web est équipée!

Maintenant ouvrez la page a port 3000, votre terminal nodeJS va afficher ‘a user connected’. Ce qui se passe c’est que lorsque la page est loadée, la librairie socket.io va envoyer un signal vers le serveur, qui intercepte le message. Si vous bne voyez rien, rafraichissez la page.

Construction de l’application de messagerie instantanée

Maintenant côté client, écrivons le code JS pour émettre les messages entrés au clavier, dans le formulaire:

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();

  var form = document.getElementById('form');
  var input = document.getElementById('input');

  form.addEventListener('submit', function(e) {
    e.preventDefault();
    if (input.value) {
      socket.emit('chat message', input.value);
      input.value = '';
    }
  });
</script>

et côté serveur on va afficher le message, ajoutez le snippet suivant :

io.on('connection', (socket) => {
...
  socket.on('chat message', (msg) => {
    console.log('message: ' + msg);
  });
});

Tapez les messages dans le formulaire, observez les logs côté serveur.

Le broadcasting côté serveur

Intéressons nous maintenant sur comment le serveur va émettre un message pour tous les client connectés, à la manière d’une station radio, d’où le terme ‘broadcasting’. Il faut utiliser la méthode emit() de l’objet io. Modifiez le code :

io.on('connection', (socket) => {
    console.log('a user connected');

    socket.on('disconnect', () => {
        console.log('user disconnected');
    });

    socket.on('chat message', (msg) => {
        console.log('message du client: ' + msg);
        io.emit('retour', { propriete1: 'valeur 1', propriete2: 'valeur2' })
    });
});

Configurer VSCode pour débugger

Il ya plusieurs façons de débugger NodeJS avec VSCode, le plus simple (mais peut être le plus ennuyant) est d’utiliser l’auto Attach, dès qu’un process NodeJS est démarré, VSCode se met en écoute. On va utiliser cette méthode. Pour plus d’information sur les autres méthodes allez sur cette page.

Utiliser les dev tools pour visualiser les communication via le socket

Vous pouvez voir les connexion socket comme vous pouviez le faire avec les requêtes Ajax. Dans les devtools, allez dans l’onglet Réseau, cliquez sur WS dans les filtres (à côté de XHR, Images etc.

Dans l’onglet Réponse, vous pouvez voir en fait les allées et venues des messages.

Retour en haut