Socket io, des données en temps réelle

Socket io, des données en temps réelle

Quand on débute dans le développement web, on pense que pour changer les données d’une page, on doit forcément la rafraichir afin que le serveur ait le temps de faire sa petite mise à jour. Puis un jour on découvre Socket.io, et le dynamisme d’une page prend tout son sens. Le temps réel, c’est l’avenir.

Ryan Djoher

19/04/2021 - 9 minutes de lecture

Et pourquoi ça ?

Si on veut afficher les données d’un utilisateur, on n’a franchement pas besoin d’utiliser du temps réel puisque aucun changement inopiné ne viendra perturber tout ça. Mais dans le cas où quelqu’un d’extérieur doit changer des informations sur votre page, Socket.io prend tout son sens. Vous l’aurez surement deviné, je parle des chats. Quand on est sur un site de vente et qu’on veut discuter avec un téléconseiller, on peut avoir une discussion sans être interrompu par notre navigateur qui veut son petit reload. C’est dans ce genre de cas de figure où Socket.io est vraiment pratique. Mais attention, il ne faut pas en abuser car en termes de ressources, c’est assez gourmand.

Installation de Socket.io

Pour illustrer mon petit tuto, on va faire un serveur très simpliste en Node. Normalement, vous devriez savoir ce que chaque ligne fait.

const express = require('express');
const app = express();
const PORT = 8889;

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

app.listen(PORT, () => console.log(`Le serveur est sur le http://localhost:${PORT}/`));

Donc si on lance notre serveur (après avoir fait notre petit installation de express via la commande $ npm i express --save), la racine de notre site devrait nous renvoyer la page index.html que voici :

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tuto Socket.io</title>
</head>

<body>
    <h1>Salut Socket.io</h1>
</body>

</html>

La première chose qu’on va voir, c’est quand le serveur va envoyer un message au client sans rechargement de page. Donc pour l’instant, on installe Socket.Io via cette commande :

$ npm install socket.io --save 

Et on le déclare dans notre fichier serveur qu’on a déjà créé :

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

Mais qu’avons-nous fait ? Un serveur dans un serveur ? cela peut paraitre déroutant mais oui, on vient de faire un second serveur. Pourquoi ? Parce que Socket.Io utilise un autre port afin qu’il ne rentre pas en collision avec celui de notre serveur Node. On va donc rajouter cette ligne :

server.listen(3000);

Et comme c’est un peu déroutant, voici le code complet du serveur Node :

const express = require('express');
const app = express();
const PORT = 8889;

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

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

server.listen(3000);

app.listen(PORT, () => console.log(`Le serveur est sur le http://localhost:${PORT}/`));

On a bien avancé, maintenant on va tout simplement capturer dans le serveur le fait qu’un client vient de se connecter via Socket.Io. Voici le code à intégrer dans notre fichier Node :

io.on('connect', (socket) => {
    console.log('Un client vient de se connecter')
});

C’est bien beau tout ça, mais il faut que le client se connecte au serveur, et plus précisément au port de Socket.Io. En premier lieu, on va ajouter le script de Socket.Io dans le client. Vous pouvez le faire via le module, via un fichier que vous hébergez dans votre ordinateur ou via le CDN. On va opter pour la dernière solution :

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>

Maintenant que notre client a accès à Socket.Io, on va maintenant lui préciser le numéro de port via cette ligne de code :

const socket = io('ws://localhost:3000');

A ce stade, voici le code complet du client :

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tuto Socket.io</title>
</head>

<body>
    <h1>Salut Socket.io</h1>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
    <script>
        const socket = io('ws://localhost:3000');
    </script>
</body>

</html>

Maintenant, faites autant de rafraichissement que vous voulez, le serveur Node verra qu’un client est présent en affichant le message 'Un client vient de se connecter' sur votre terminal. Bon, on n’est pas encore au temps réel car il faut faire un rafraichissement, mais sachez que grâce à ça, on peut savoir qui est présent et surtout le nombre de client.

Et ce message du serveur au client, c’est pour quand ?

Revenons à nos moutons et entrons enfin dans le vif du sujet. Dans le fichiers serveur Node, on remarque qu’on a maintenant une fonction io.on qui porte le nom de « connect ». C’est dans cette même fonction qu’on va envoyer un message au qui sera intercepté par le client sans rechargement.

io.on('connect', (socket) => {
    socket.emit('message', 'Vous êtes bien connecté ! Ce message est juste pour la page qui a fait l\'appel')
});

Le serveur a la fonction pour envoyer un message, il faut maintenant une fonction coté client pour recevoir ce message.

        socket.on('message', function (message) {
            console.log('Le serveur a un message pour vous : ' + message);
        })

Notez que le premier argument, c’est le mot « message ». Et si vous regardez le code coté serveur, vous voyez aussi l’argument « message » dans la fonction. Pour que le lien entre les deux se fassent, ils doivent avoir le même argument. Je vous donne le code complet.

const express = require('express');
const app = express();
const PORT = 8889;

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

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

io.on('connect', (socket) => {
    console.log('Un client vient de se connecter')
    socket.emit('message', 'Vous êtes bien connecté ! Ce message est juste pour la page qui a fait l\'appel')
});


server.listen(3000);

app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}/`));
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tuto Socket.io</title>
</head>

<body>
    <h1>Salut Socket.io</h1>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
    <script>
        const socket = io('ws://localhost:3000');

        socket.on('message', function (message) {
            console.log('Le serveur a un message pour vous : ' + message);
        })

    </script>
</body>

</html>

Et quand c’est le client qui dérange le serveur ?

Pour faire la même chose mais dans le sens contraire, ce n’est pas si différent. Coté serveur, au lieu d’utiliser la fonction « emit » (qui envoie), on utilise la fonction « on » (qui écoute).

io.on('connect', (socket) => {
    socket.on('message3', function (message) { 
        console.log('Un client me parle ! Il me dit : ' + message);
    })
});

Et c’est coté client qu’on va utiliser la fonction « emit ».

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

Et voilà ! Ce n’est vraiment pas si sorcier quand on a compris comment fonctionne Socket.Io. Mais tout n’est pas si simple, il y a parfois quelques utilisations un peu plus poussées.

Le boadcast

Parfois, on veut envoyer un message au serveur et le serveur doit renvoyer le message aux autres utilisateurs. Sauf que dans le cas de figure qu’on a vu plus haut, le serveur envoie à tous les clients, même à celui qui l’a envoyé en premier. Et franchement, c’est un peu inutile de recevoir notre propre message.

Pour que le serveur envoie le message à tous les clients, sauf celui qui a envoyé e message, on va utiliser la fonction broadcast coté serveur.

io.on('connect', (socket) => {
    socket.broadcast.emit("message2", "ce message est pour toutes les pages, sauf celui qui l'envoi")
});

Pour le coté client, c’est exactement comme on a vu plus haut, on va juste envoyer le message de client à serveur.

Maintenant on va voir un autre cas de figue que vous allez beaucoup utiliser. Je pense même que c’est ce que vous utiliserez le plus avec Socket.Io.

Soirée privée

Un chat, c’est deux personnes (ou plus) qui discutent. Et leur discussion ne doit pas être public car seuls les utilisateurs doivent y avoir accès. C’est ici qu’entre en jeu les « room ». Pour schématiser, c’est une pièce privée où seuls les gens possédant une invitation peut y aller et discuter entre eux.

D’abord, le client va demander la création d’un « room » auprès du serveur et y adhérer automatiquement.

socket.emit('join_room', 'myRoom');

Comme d’habitude, le premier argument est une chaine de charactère qui fait le lien et le second est le nom qu’on envoie. Il faut maintenant que le serveur reçoive l’information.

io.on('connect', (socket) => {
    socket.on(' myRoom ', room => {
        socket.join(room);
    });
});

Le serveur vient de créer le salon « myRoom » et il y a mis la personne qui a fait la demande de création. Bien sûr, ne le laisser pas tout seul le pauvre. Pour y rajouter des gens, c’est exactement la même chose. Le serveur saura que le salon existe déjà donc il ne fera pas un doublon. Et bien entendu, il y incorpora toutes les personnes qui y feront la demande.

Si on veut partir du salon, rien de plus simple :

io.on('connect', (socket) => {
    socket.on('myRoom', room => {
        socket.leave(room)
    });
});

Avec la fonction « leave », on peut faire quitter le client qu’on veut.

Envoyer un message au salon

Si un client qui fait préalablement parti d’un salon décide d’envoyer un message un message aux autres personnes du salon, voici la marche à suivre. Tout d’abord, voici la fonction coté client :

socket.emit('sendMessage', { room: 'myRoom', message: 'Hello'});

Et coté serveur, on va réceptionner le message. Comme on l’a vu, le premier argument est une chaine de charactère qui va lier la fonction client / serveur.

io.on('connect', (socket) => {
    client.on('sendMessage', function(data) {
        io.sockets.in(data.room).emit('message', data.message);
    });
});

La fonction « in » permet de spécifier le nom du salon (qui est en paramètre dans l’objet) et tout de suite après, on envoie le message avec la fonction emit. N’oubliez pas que coté client, on devra faire une fonction « socket.on » qui écoutera le serveur. Mais à ce moment, vous devriez vous en sortir sans problème.

A ce stade, vous savez les principales utilisations de Socket.Io. Comme d’habitude, on peut creuser le sujet pour voir toute la force de ce module mais si vous faites comme 90% des utilisateurs, ce petit tuto devrait suffire.