HTML - Événements envoyés par le serveur

-

Comment fonctionnent les événements envoyés par le serveur

Les événements envoyés par le serveur (SSE) sont une technologie qui permet à un serveur d'envoyer des données à un navigateur client en temps réel. L'architecture des SSE repose sur un modèle de communication unidirectionnel, où le serveur envoie des données au client, mais le client n'envoie pas de données au serveur.

Dans les SSE, le client se connecte au serveur en utilisant l'API EventSource. Le serveur maintient cette connexion ouverte et envoie des événements au client chaque fois que de nouvelles données sont disponibles. Le client reçoit ces événements et les traite au fur et à mesure de leur arrivée, mettant à jour la page web ou déclenchant d'autres actions selon les besoins.

La communication entre le client et le serveur dans les SSE se fait via une connexion HTTP standard. Le serveur envoie les événements sous forme de flux de données textuelles, chaque événement étant séparé par une ligne vide. Le client utilise l'API EventSource pour écouter ces événements et les gérer en conséquence.

Conseil: Exemple d'une connexion EventSource

const eventSource = new EventSource('http://example.com/sse');

eventSource.onmessage = function(event) {
    console.log(event.data);
};

Une différence clé entre les SSE et les WebSockets est le modèle de communication. Alors que les SSE sont unidirectionnels, avec le serveur envoyant des données au client, les WebSockets permettent une communication bidirectionnelle, où le client et le serveur peuvent s'envoyer des données mutuellement.

Conseil: Exemple d'une connexion WebSocket

const socket = new WebSocket('ws://example.com/ws');

socket.onopen = function(event) {
    socket.send('Hello Server!');
};

socket.onmessage = function(event) {
    console.log(event.data);
};

Une autre différence réside dans le format des messages. Dans les SSE, les messages sont envoyés sous forme de texte brut, chaque message consistant en une ou plusieurs lignes de données textuelles. En revanche, les WebSockets utilisent un format de message binaire, qui peut être plus efficace pour l'envoi de grandes quantités de données.

Malgré ces différences, les SSE et les WebSockets ont tous deux leur place dans le développement web moderne. Les SSE conviennent bien aux scénarios où le serveur doit envoyer des mises à jour au client en temps réel, comme les fils d'actualités ou les tickers boursiers. Les WebSockets, quant à eux, sont idéaux pour les applications nécessitant une communication bidirectionnelle, comme les applications de chat ou les jeux multijoueurs.

Configuration des événements envoyés par le serveur

La configuration des événements envoyés par le serveur implique de paramétrer le serveur et le client. Côté serveur, vous devez mettre en place un script qui envoie des événements au client. Côté client, vous devez créer un objet EventSource pour recevoir et gérer ces événements.

Configuration côté serveur

Pour configurer le serveur afin qu'il envoie des événements, vous devez mettre en place un script qui répond aux requêtes du client avec les en-têtes appropriés et les données d'événement. Le serveur doit envoyer une réponse avec un en-tête Content-Type de "text/event-stream", qui indique au client qu'il doit s'attendre à un flux d'événements.

Conseil: Code côté serveur utilisant Node.js et le framework Express

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

app.get('/sse', (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    setInterval(() => {
        const eventData = `data: L'heure du serveur est ${new Date()}\n\n`;
        res.write(eventData);
    }, 1000);
});

app.listen(3000, () => {
    console.log('Serveur démarré sur le port 3000');
});

Le serveur envoie un nouvel événement au client toutes les secondes, contenant l'heure actuelle du serveur.

Conseil: Code côté serveur utilisant PHP

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

$time = date('r');
echo "data: L'heure du serveur est : {$time}\n\n";
flush();
?>

Le script PHP envoie un seul événement au client, contenant l'heure actuelle du serveur.

Configuration côté client

Côté client, vous devez créer un objet EventSource et spécifier l'URL du script côté serveur qui envoie les événements. L'objet EventSource se connectera automatiquement au serveur et commencera à recevoir les événements.

Conseil: Code côté client utilisant JavaScript

const eventSource = new EventSource('/sse');

eventSource.onmessage = (event) => {
    console.log(event.data);
};

eventSource.onerror = (error) => {
    console.error('Erreur EventSource :', error);
};

Le client crée un objet EventSource et spécifie l'URL du script côté serveur ('/sse'). Le gestionnaire d'événements onmessage est appelé chaque fois qu'un nouvel événement est reçu, et le gestionnaire d'événements onerror est appelé si une erreur se produit.

Vous pouvez également gérer des types d'événements spécifiques en ajoutant des écouteurs d'événements :

Conseil: Gestion de types d'événements spécifiques

eventSource.addEventListener('timestamp', (event) => {
    console.log('Événement timestamp :', event.data);
});

Le client écoute les événements de type 'timestamp' et enregistre les données de l'événement dans la console.

Avec le code côté serveur et côté client en place, votre application est maintenant configurée pour utiliser les événements envoyés par le serveur pour une communication en temps réel.

Envoi et réception d'événements

Envoi d'événements depuis le serveur

Pour envoyer des événements du serveur au client en utilisant les Server Sent Events (SSE), vous devez créer et envoyer des messages d'événements formatés. Les événements comportent une ou plusieurs lignes de texte, chaque ligne étant séparée par deux-points et un espace. Chaque événement se termine par un double caractère de nouvelle ligne (\n\n).

Les champs d'événements les plus courants sont :

Champ Description
event Spécifie le type d'événement. Si omis, le type d'événement par défaut est "message".
data Contient les données de l'événement. Vous pouvez envoyer plusieurs lignes de données en incluant plusieurs champs data.
id Attribue un ID unique à l'événement. Cela aide à la reconnexion et à la gestion des erreurs.
retry Définit le temps de reconnexion (en millisecondes) pour le client si la connexion est perdue.

Conseil: Envoi d'événements côté serveur

app.get('/sse', (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    const sendEvent = (eventType, data) => {
        const eventData = `event: ${eventType}\ndata: ${JSON.stringify(data)}\n\n`;
        res.write(eventData);
    };

    // Envoie un événement "timestamp" chaque seconde
    setInterval(() => {
        const timestamp = new Date().toLocaleTimeString();
        sendEvent('timestamp', { time: timestamp });
    }, 1000);

    // Envoie un événement "message" lorsqu'une condition est remplie
    if (someCondition) {
        sendEvent('message', { text: 'Bonjour du serveur !' });
    }
});

Réception d'événements côté client

Côté client, vous pouvez écouter les événements en utilisant l'objet EventSource. Le gestionnaire d'événements onmessage est appelé lorsqu'un événement "message" est reçu, et vous pouvez définir des gestionnaires d'événements personnalisés pour des types d'événements spécifiques en utilisant addEventListener.

Conseil: Écoute d'événements côté client

const eventSource = new EventSource('/sse');

eventSource.onmessage = (event) => {
    const data = JSON.parse(event.data);
    console.log('Message reçu :', data.text);
};

eventSource.addEventListener('timestamp', (event) => {
    const data = JSON.parse(event.data);
    console.log('Horodatage reçu :', data.time);
});

Vous pouvez gérer différents types d'événements en ajoutant plus d'écouteurs d'événements :

Conseil: Gestion de plusieurs types d'événements

eventSource.addEventListener('userConnected', (event) => {
    const data = JSON.parse(event.data);
    console.log(`L'utilisateur ${data.userId} s'est connecté`);
});

eventSource.addEventListener('userDisconnected', (event) => {
    const data = JSON.parse(event.data);
    console.log(`L'utilisateur ${data.userId} s'est déconnecté`);
});

En envoyant et en recevant des événements, votre application peut établir un canal de communication en temps réel entre le serveur et le client, vous permettant de mettre à jour le client avec de nouvelles données dès qu'elles sont disponibles sur le serveur.

Gestion des erreurs et reconnexion

Lors de l'utilisation des événements envoyés par le serveur (SSE), il est important de gérer les erreurs et de se reconnecter au serveur en cas de perte de connexion. Cela permet de créer une application plus fiable et robuste.

La gestion des erreurs côté client implique l'écoute de l'événement onerror sur l'objet EventSource. Cet événement est déclenché lorsqu'une erreur se produit, comme un problème de réseau ou un problème avec le script côté serveur.

Conseil: Gérer les erreurs côté client

const eventSource = new EventSource('/sse');

eventSource.onerror = (error) => {
    console.error('Erreur EventSource:', error);
    // Effectuer la gestion des erreurs, comme mettre à jour l'interface utilisateur ou enregistrer l'erreur
};

La reconnexion au serveur après une perte de connexion est un autre aspect important de la gestion des erreurs. Par défaut, l'objet EventSource essaiera de se reconnecter au serveur si la connexion est fermée. Cependant, vous pouvez contrôler le comportement de reconnexion en utilisant les propriétés withCredentials et reconnectionTime.

Conseil: Contrôler le comportement de reconnexion

const eventSource = new EventSource('/sse', {
    withCredentials: true,
    reconnectionTime: 5000
});

La propriété withCredentials, lorsqu'elle est définie sur true, envoie les cookies et les en-têtes d'authentification avec la requête EventSource, ce qui peut être utile pour maintenir une session lors des reconnexions.

La propriété reconnectionTime définit le temps (en millisecondes) à attendre avant de tenter de se reconnecter au serveur. Par défaut, l'objet EventSource utilisera une stratégie de backoff exponentiel, où le temps de reconnexion augmente à chaque tentative échouée.

Bonnes pratiques pour la gestion des erreurs et la reconnexion
1. Implémenter la gestion des erreurs côté client : Écouter l'événement onerror et gérer les erreurs de manière élégante, par exemple en mettant à jour l'interface utilisateur ou en enregistrant l'erreur.
2. Définir un temps de reconnexion approprié : Choisir un temps de reconnexion qui équilibre le besoin de mises à jour en temps réel avec le risque de surcharger le serveur par trop de tentatives de reconnexion.
3. Utiliser des battements de cœur côté serveur : Implémenter des battements de cœur côté serveur (messages périodiques envoyés par le serveur) pour détecter et gérer plus rapidement les connexions perdues.
4. Gérer les erreurs côté serveur : S'assurer que votre script côté serveur peut gérer les erreurs et envoyer des messages d'erreur appropriés au client.
5. Utiliser des mécanismes de repli : Si SSE n'est pas pris en charge ou si la connexion échoue, envisager de recourir à des méthodes alternatives, comme le long-polling ou les WebSockets.

Prise en charge des navigateurs et solutions de repli

Les Server Sent Events (SSE) sont pris en charge par la plupart des navigateurs web modernes, notamment Chrome, Firefox, Safari et Edge. Cependant, certains navigateurs plus anciens peuvent ne pas prendre en charge nativement les SSE, ce qui signifie que vous devez prévoir des options de repli pour maintenir la compatibilité.

En 2023, la prise en charge globale des SSE par les navigateurs est d'environ 95%, selon Can I Use. Cela signifie que la majorité des utilisateurs pourront utiliser les SSE sans problème. Néanmoins, il est important de prévoir des options de repli pour les 5% d'utilisateurs restants qui pourraient utiliser des navigateurs plus anciens ou moins courants.

Une option de repli pour les navigateurs qui ne prennent pas en charge les SSE est d'utiliser le long-polling. Le long-polling est une technique où le client envoie une requête au serveur et attend une réponse. Si le serveur n'a pas de nouvelles données, il garde la requête ouverte jusqu'à ce que de nouvelles données soient disponibles, puis renvoie la réponse au client. Le client envoie alors immédiatement une autre requête, répétant ainsi le processus. Bien que moins efficace que les SSE, le long-polling peut offrir un niveau similaire de fonctionnalité en temps réel.

Une autre option de repli est d'utiliser les WebSockets. Les WebSockets fournissent un canal de communication bidirectionnel entre le client et le serveur, permettant un transfert de données en temps réel dans les deux sens. Si un navigateur ne prend pas en charge les SSE, vous pouvez vérifier s'il prend en charge les WebSockets et les utiliser comme solution de repli.

Si vous préférez une approche plus transparente, vous pouvez utiliser des polyfills ou des bibliothèques qui offrent une prise en charge des SSE pour les navigateurs plus anciens. Un polyfill est un morceau de code qui reproduit la fonctionnalité d'une API de navigateur plus récente dans un navigateur plus ancien. Par exemple, le polyfill EventSource de Yaffle peut être utilisé pour ajouter la prise en charge des SSE aux navigateurs plus anciens qui n'ont pas de support natif.

Il existe également des bibliothèques comme Pusher et Socket.IO qui offrent des fonctionnalités en temps réel en utilisant diverses techniques, notamment les SSE, les WebSockets et le long-polling. Ces bibliothèques peuvent abstraire les différences entre les navigateurs et fournir une API cohérente pour la communication en temps réel.

Conseil: Utilisation d'un polyfill pour ajouter la prise en charge des SSE aux navigateurs plus anciens

<!-- Inclure le polyfill EventSource -->
<script src="https://cdn.jsdelivr.net/npm/event-source-polyfill@1.0.9/src/eventsource.min.js"></script>

<script>
if (!window.EventSource) {
    // Si le navigateur ne prend pas en charge EventSource, utiliser le polyfill
    window.EventSource = window.EventSourcePolyfill;
}

const eventSource = new EventSource('/sse');

eventSource.onmessage = (event) => {
    console.log(event.data);
};
</script>

En incluant le polyfill EventSource et en vérifiant si le navigateur prend en charge EventSource nativement, vous pouvez vous assurer que la fonctionnalité SSE est disponible pour tous les utilisateurs, quel que soit leur navigateur.

En résumé, bien que les SSE soient pris en charge par la plupart des navigateurs modernes, il est important de prévoir des options de repli et des polyfills pour les navigateurs plus anciens. En utilisant des techniques comme le long-polling, les WebSockets, ou des bibliothèques qui abstraient les différences entre navigateurs, vous pouvez offrir une expérience en temps réel cohérente à tous vos utilisateurs.

Avantages et Inconvénients des SSE

Les Server Sent Events (SSE) présentent plusieurs avantages et inconvénients par rapport à d'autres technologies de communication en temps réel comme les WebSockets et le long-polling.

Avantages

Un des principaux avantages des SSE est qu'ils permettent d'obtenir des mises à jour en temps réel du serveur sans avoir besoin d'interroger celui-ci de manière répétée. Avec les SSE, le serveur peut envoyer des mises à jour au client dès que de nouvelles données sont disponibles, ce qui signifie que le client dispose toujours des informations les plus récentes sans avoir à envoyer de requêtes au serveur.

Les SSE sont également plus légers que les WebSockets. Les WebSockets utilisent une connexion persistante et bidirectionnelle entre le client et le serveur, ce qui peut être excessif dans les situations où vous avez seulement besoin que le serveur envoie des mises à jour au client. Les SSE, en revanche, utilisent une connexion HTTP standard pour envoyer des mises à jour, ce qui signifie qu'ils ont moins de surcharge et peuvent être plus efficaces en termes de ressources serveur.

Un autre avantage des SSE est qu'ils sont plus simples à mettre en œuvre que les WebSockets. Avec les SSE, vous n'avez besoin que de configurer un script côté serveur qui envoie des événements au client, et un script côté client qui écoute ces événements en utilisant l'API EventSource. Les WebSockets, quant à eux, nécessitent une configuration plus complexe impliquant un processus de handshake et une mise à niveau du protocole.

Inconvénients

Un inconvénient des SSE est qu'ils ne prennent en charge que la communication unidirectionnelle du serveur vers le client. Cela signifie que le client ne peut pas envoyer de messages au serveur en utilisant les SSE. Si vous avez besoin d'une communication bidirectionnelle, vous devrez utiliser les WebSockets ou une autre technologie qui le permet.

Un autre inconvénient des SSE est qu'ils ont une prise en charge limitée par les navigateurs par rapport à d'autres technologies comme le long-polling. Bien que la plupart des navigateurs modernes prennent en charge les SSE, certains navigateurs plus anciens n'ont pas de support natif pour ceux-ci. Cela signifie que vous pourriez devoir fournir des options de repli ou utiliser des polyfills pour vous assurer que votre application fonctionne sur tous les navigateurs.

Conseil: HTML code with extra spaces

<p>This    is   a   paragraph   with    extra   spaces.</p>

Lorsqu'un navigateur rend ce code, il affichera le texte comme suit :

This is a paragraph with extra spaces.

Conseil: Mismatched tags

<p>This is a paragraph.</div>

Dans ce cas, la balise d'ouverture <p> est fermée avec une balise </div>, ce qui est incorrect. La bonne façon de fermer le paragraphe est :

<p>This is a paragraph.</p>

Cas d'utilisation et exemples de SSE

Les Server-Sent Events (SSE) conviennent à de nombreux scénarios d'applications web en temps réel. Examinons quelques cas d'utilisation courants et des exemples où les SSE peuvent être utilisés pour offrir une meilleure expérience utilisateur.

Un cas d'utilisation courant des SSE concerne les mises à jour de données en temps réel. Une application de cotation boursière pourrait utiliser les SSE pour envoyer des mises à jour en temps réel des cours des actions au client. Chaque fois qu'un cours change sur le serveur, celui-ci peut envoyer un événement au client avec le prix mis à jour. Le client peut alors actualiser l'interface utilisateur pour afficher le nouveau prix sans que l'utilisateur ait besoin de rafraîchir la page.

Conseil: Mises à jour boursières en temps réel

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

// Données boursières fictives
let stockData = {
    'AAPL': 150.23,
    'GOOGL': 2321.01,
    'MSFT': 300.45
};

// Envoyer des mises à jour boursières toutes les 5 secondes
setInterval(() => {
    // Mettre à jour les cours de manière aléatoire
    for (const symbol in stockData) {
        stockData[symbol] += Math.random() * 10 - 5;
    }

    // Envoyer les données boursières mises à jour aux clients
    sendEvent('stockUpdate', stockData);
}, 5000);

// ...
const eventSource = new EventSource('/sse');

eventSource.addEventListener('stockUpdate', (event) => {
    const stockData = JSON.parse(event.data);

    for (const symbol in stockData) {
        const price = stockData[symbol].toFixed(2);
        document.getElementById(symbol).textContent = price;
    }
});

Un autre cas d'utilisation des SSE est l'envoi de notifications et d'alertes au client. Une application de réseau social pourrait utiliser les SSE pour notifier les utilisateurs lorsqu'ils reçoivent un nouveau message ou lorsque quelqu'un les mentionne dans une publication. Le serveur peut envoyer un événement au client chaque fois qu'une nouvelle notification est disponible, et le client peut afficher la notification à l'utilisateur.

Les SSE peuvent également être utilisés pour des applications de chat en direct. Lorsqu'un utilisateur envoie un message, le serveur peut diffuser ce message à tous les clients connectés en utilisant les SSE. Chaque client écouterait les nouveaux événements de message et mettrait à jour l'interface utilisateur du chat en conséquence. Cependant, gardez à l'esprit que les SSE sont unidirectionnels, vous auriez donc besoin d'utiliser une autre méthode (comme une requête HTTP POST classique) pour envoyer des messages du client au serveur.

Les SSE peuvent être utilisés pour des analyses et des métriques en temps réel. Une application de surveillance de serveur pourrait utiliser les SSE pour envoyer des métriques de serveur en temps réel au client. Le client pourrait afficher ces métriques dans un tableau de bord, donnant aux administrateurs une vue en temps réel des performances du serveur.

Conseil: Métriques de serveur en temps réel

// Envoyer les métriques du serveur toutes les secondes
setInterval(() => {
    const serverMetrics = {
        cpu: os.loadavg()[0],
        memory: os.freemem() / os.totalmem()
    };

    sendEvent('serverMetrics', serverMetrics);
}, 1000);
eventSource.addEventListener('serverMetrics', (event) => {
    const metrics = JSON.parse(event.data);

    document.getElementById('cpuUsage').textContent = `${metrics.cpu.toFixed(2)}%`;
    document.getElementById('memoryUsage').textContent = `${(metrics.memory * 100).toFixed(2)}%`;
});

Voici quelques exemples de la façon dont les SSE peuvent être utilisés dans des applications web en temps réel. La nature unidirectionnelle des SSE les rend adaptés aux scénarios où le serveur doit envoyer des mises à jour au client, mais où le client n'a pas besoin de renvoyer des données au serveur.