Chapitre 8.12Le WebSocket et le temps réel

Quand le serveur prend l'initiative.

4 minutes de lecture

Quand tu utilises un chat en ligne, WhatsApp Web, Slack ou Discord, les messages des autres apparaissent sur ton écran sans que tu aies besoin de rafraîchir la page.

Pourtant, le protocole HTTP vu dans les chapitres précédents ne fonctionne pas comme ça. HTTP repose sur un modèle de requête et de réponse, le client demande, le serveur répond, et l'échange s'arrête là. Le serveur n'a aucun moyen de prendre l'initiative d'envoyer quelque chose au client, tout simplement parce qu'il ne sait pas comment le joindre.

Comment faire alors pour que le serveur puisse pousser des informations en temps réel ? Trois réponses techniques existent, et elles cohabitent toujours.

Le polling, la solution naïve

La première approche s'appelle le polling, ou interrogation périodique. Le client envoie une requête au serveur toutes les X secondes pour vérifier s'il y a du nouveau.

"Hey serveur, j'ai de nouveaux messages ?" Non.
"Hey serveur, j'ai de nouveaux messages ?" Non.
"Hey serveur, j'ai de nouveaux messages ?" Oui, en voilà un.

C'est simple, ça marche partout, et ça reste parfaitement acceptable pour beaucoup de besoins. Mais le gaspillage est évident, l'immense majorité des requêtes ne rapportent rien tout en consommant de la bande passante et du temps serveur. Sur mobile, ce bavardage permanent vide aussi la batterie. Et il subsiste toujours un délai entre l'événement réel et sa détection, qui vaut en moyenne la moitié de l'intervalle choisi.

Une variante astucieuse existe, le long polling. Le client pose sa question, et au lieu de répondre "non" immédiatement, le serveur garde la requête ouverte jusqu'à ce qu'il ait effectivement quelque chose à annoncer, ou qu'un délai maximum expire. Le client relance aussitôt une nouvelle requête. On obtient un quasi temps réel sans rien changer à l'infrastructure, au prix de connexions maintenues ouvertes en permanence côté serveur. C'est la technique qui a fait tourner les premiers chats web, et elle sert encore de solution de repli quand rien d'autre ne passe.

Le WebSocket, une connexion permanente

Le WebSocket, standardisé en 2011, résout le problème d'une autre manière, en établissant une connexion bidirectionnelle permanente entre le client et le serveur.

Au lieu du modèle "je demande, tu réponds, on se quitte", le WebSocket ouvre un canal qui reste ouvert. Le client et le serveur peuvent s'envoyer des messages à tout moment, dans les deux sens, sans rétablir quoi que ce soit. C'est la différence entre s'écrire des lettres et décrocher le téléphone.

Le plus élégant, c'est la façon dont la connexion démarre. Le client envoie une requête HTTP tout à fait ordinaire, avec un en-tête qui demande poliment de changer de protocole. Si le serveur est d'accord, il répond avec le code HTTP 101, "Switching Protocols", et la même connexion réseau bascule alors en WebSocket. Cette poignée de main initiale en HTTP est ce qui permet au WebSocket de traverser les pare-feux et les infrastructures web existantes, là où un protocole complètement à part aurait été bloqué partout.

Tu croiseras deux adresses, ws:// pour une connexion en clair et wss:// pour une connexion chiffrée. En production, seule la seconde est acceptable, exactement comme HTTPS a remplacé HTTP.

Les cas d'usage

Le WebSocket est présent partout où le temps réel compte vraiment.

  • Le chat : Slack, Discord, WhatsApp Web. Les messages arrivent instantanément, et l'indicateur "untel est en train d'écrire" n'existerait pas sans canal permanent.
  • Les notifications en direct : les alertes qui apparaissent dans une messagerie ou un réseau social sans recharger la page.
  • Les jeux en ligne : chaque mouvement d'un joueur doit être transmis aux autres en quelques dizaines de millisecondes. Attention, ça vaut surtout pour les jeux de navigateur et les jeux au tour par tour. Les jeux d'action très nerveux, eux, préfèrent UDP, qui accepte de perdre un message plutôt que d'attendre qu'on le renvoie.
  • La collaboration en temps réel : Google Docs, Figma. Plusieurs personnes travaillent sur le même document et voient les modifications des autres, curseur compris, au fil de la frappe.
  • Les données financières : les cours de bourse qui se mettent à jour en continu, où un prix vieux de quelques secondes n'a déjà plus aucune valeur.
  • Le suivi de position : le livreur ou le chauffeur dont tu vois l'icône se déplacer sur la carte.

Server-Sent Events, le compromis

Il existe une solution intermédiaire, les Server-Sent Events, ou SSE. Comme avec le WebSocket, le serveur peut pousser des données vers le client sans que celui-ci les demande. La différence tient à ce que la communication est unidirectionnelle, seul le serveur émet. Si le client a quelque chose à dire, il fait une requête HTTP classique à côté.

En échange de cette limitation, SSE apporte de vrais avantages. Il reste du HTTP ordinaire, donc il traverse sans difficulté les proxys et les caches d'entreprise qui bloquent parfois le WebSocket. Il gère tout seul la reconnexion automatique quand le réseau coupe, ce qu'il faut coder soi-même en WebSocket. Et il est nettement plus simple à mettre en oeuvre côté serveur.

C'est largement suffisant pour un flux de notifications, la mise à jour d'un tableau de bord, une barre de progression d'export, ou le streaming de réponses d'une intelligence artificielle. C'est d'ailleurs ce mécanisme qui fait apparaître les réponses de ChatGPT ou de Claude mot par mot plutôt que d'un bloc.

Ne confonds pas tout ça avec les webhooks, qui répondent à un besoin voisin mais entre serveurs. Là, c'est ton serveur qui expose une adresse, et le service tiers vient l'appeler quand un événement survient, par exemple un paiement validé. Le principe du "ne m'appelle pas, je t'appellerai" est le même, mais il n'y a aucun navigateur dans l'histoire et aucune connexion maintenue.

Ce que ça coûte côté technique

Le temps réel n'est pas gratuit, et c'est utile de le savoir avant de le demander.

Une connexion permanente immobilise des ressources sur le serveur pendant toute sa durée, même quand rien ne circule. Dix mille utilisateurs connectés, ce sont dix mille connexions ouvertes à maintenir. C'est parfaitement faisable, mais cela se dimensionne et se surveille.

Plus subtil, une connexion WebSocket est rattachée à un serveur précis. Si ton infrastructure répartit le trafic sur plusieurs machines, deux utilisateurs du même salon de discussion peuvent se retrouver sur des serveurs différents, qui ne savent rien l'un de l'autre. Il faut alors un mécanisme pour faire circuler les messages entre eux, typiquement une file de messages. C'est le vrai chantier d'un projet temps réel, bien plus que l'ouverture de la connexion.

Enfin, les connexions se coupent. Passage du Wi-Fi à la 5G, tunnel, mise en veille du téléphone, coupure par un équipement réseau qui juge la connexion inactive. Une application sérieuse envoie donc régulièrement un petit signal de vie pour maintenir le canal, détecte les ruptures, se reconnecte automatiquement et rattrape les messages manqués pendant la coupure. Des bibliothèques comme Socket.IO existent précisément pour prendre en charge cette plomberie, avec un repli sur le long polling quand le WebSocket est bloqué.

Que choisir

La règle est assez simple, et elle va du moins cher au plus cher.

Le polling suffit dès qu'un délai de quelques secondes ou quelques dizaines de secondes est acceptable, et il ne demande aucune infrastructure particulière. SSE s'impose quand seul le serveur a des choses à annoncer, ce qui couvre la majorité des besoins de notification et de tableau de bord. Le WebSocket devient justifié quand les deux parties doivent parler en permanence et vite, chat, collaboration, jeu.

De mon expérience, l'erreur la plus courante consiste à choisir le WebSocket par principe, parce que c'est la solution la plus moderne, pour un besoin qu'un rafraîchissement toutes les trente secondes aurait réglé. On hérite alors de toute la complexité de reconnexion et de répartition, sans aucun bénéfice pour l'utilisateur. Demande toujours quel délai est réellement inacceptable pour le métier avant de choisir la technique.

PrécédentLa programmation objet Tous les chapitres