Les server-sent events (SSE)

Les Server-Sent Events (SSE en abrégé) sont une technologie Web servant à ouvrir des connexions unidirectionnelles, du serveur vers le client. C’est une normalisation des techniques qui étaient bricolées par le passé pour faire du « server push » (comme le long polling).

Les SSE sont une alternative intéressante aux websockets. En effet, les websockets dépendent d’un protocole spécifique, et donc d’un serveur particulier. En PHP, on utilise habituellement Ratchet pour cela ; mais même s’il existe pas mal de documentation à ce sujet, cela reste une brique technologique supplémentaire à mettre en œuvre, on n’a pas toujours la possibilité − ou l’envie − de l’utiliser.

Il faut néanmoins comprendre la différence majeure entre les deux : les websockets sont bidirectionnelles, le client et le serveur peuvent échanger entre eux dans les deux sens ; les SSE ne permettent que la communication du serveur vers le client.
Mais en pratique on se rend compte que c’est rarement un vrai problème. Si on prend l’exemple typique de la discussion en ligne, l’important est surtout de recevoir en temps réel les nouveaux messages écrits par les autres utilisateurs ; faire une requête AJAX pour envoyer les messages que l’on écrit n’est pas un souci.

Autre chose à prendre en considération : l’utilisation des SSE fait que les clients gardent une connexion HTTP ouverte avec serveur. Il y a donc autant de connexions que d’utilisateurs connectés simultanément, et si le nombre de connexions supporté par le serveur n’est pas suffisant, votre site ne pourra plus répondre aux requêtes.

Par ailleurs, en cas de déconnexion, le client tentera automatiquement de se reconnecter au serveur, vous n’avez pas à gérer cette fonctionnalité.

Enfin, sachez que les SSE sont supportés par tous les navigateurs modernes, et pour les autres il existe un polyfill.

Implémentation simple

Pour expliquer le fonctionnement des SSE, on va prendre un exemple très très simple : une page web qui affiche une liste, avec un code Javascript qui se connecte à un canal SSE qui va lui envoyer des messages toutes les 2 secondes, chaque message étant ajouté à la liste.

Voici le code HTML/Javascript :

<html>
<head>
    <script>
        // connexion à la source SSE
        const evtSource = new EventSource("/ssedemo.php");

        // écoute des événements envoyés sur le canal "msg"
        evtSource.addEventListener("msg", function(event) {
            // création de l'élément DOM <li>
            const newElement = document.createElement("li");
            // ajout du message reçu en contenu textuel de l'élément créé
            newElement.textContent = event.data;
            // ajout de l'élément à la liste dans la page
            document.getElementById("liste").appendChild(newElement);
        });
    </script>
</head>
<body>
    <!-- la liste des messages -->
    <ul id="liste">
    </ul>
</body>
</html>Langage du code : JavaScript (javascript)

Et le code PHP du fichier “ssedemo.php” qui se trouve à la racine (pour répondre à l’URL /ssedemo.php) :

<?php

header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");

$i = 1;
// boucle infinie
while (true) {
    // vérification que la connexion est toujours active
    if (connection_aborted()) {
        // la connexion est coupée, arrêt des traitements
        break;
    }

    // envoi de l'événement dans le canal "msg"
    print("event: msg\r\n");
    print("data: Message n°$i\r\n");
    print("\r\n");

    // vidage des tampons de sortie
    ob_flush();
    flush();

    // incrémentation du compteur puis attente de 2 secondes avant
    // l'envoi du message suivant
    $i++;
    sleep(2);
}Langage du code : PHP (php)

Comme vous pouvez le voir, rien de sorcier. Vraiment, c’est très simple.
Dans cet exemple, le code PHP va envoyer des événements toutes les deux secondes, qui vont contenir chacun une chaîne de caractères (“Message n°1”, “Message n°2”, etc.). Le code Javascript va recevoir les événements, et ajouter des éléments à une liste HTML, chaque élément contenant la chaîne de caractère envoyée.

Envoi d’événements complexes

La première chose que l’on peut souhaiter faire, c’est d’envoyer des données plus complexes qu’une simple chaîne de caractères. Rien de plus simple, il suffit de sérialiser les données en JSON côté serveur, et de les désérialiser côté client.

Si on reprend l’exemple précédent, et qu’on souhaite envoyer la date courante en plus du message textuel, il faudrait juste modifier la ligne qui envoie les données de l’événement (dans le code PHP) :

<?php

header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");

$i = 1;
while (true) {
    if (connection_aborted())
        break;

    // création d'un tableau associatif avec les clés "date" et "message"
    $event = [
        'date'    => date(DATE_W3C),
        'message' => "Message n°$i",
    ];
    // envoi de l'événement sérialisé en JSON
    print("event: msg\r\n");
    print("data: " . json_encode($event) . "\r\n");
    print("\r\n");

    ob_flush();
    flush();
    $i++;
    sleep(2);
}Langage du code : PHP (php)

Et adapter le code Javascript :

<script>
    const evtSource = new EventSource("/ssedemo.php");
    evtSource.addEventListener("msg", function(event) {
        const newElement = document.createElement("li");

        // désérialisation de l'événement reçu
        var evtData = JSON.parse(event.data);
        // ajout de la date et du message dans la liste
        newElement.textContent = evtData.date + " : " + evtData.message;

        document.getElementById("liste").appendChild(newElement);
    });
</script>Langage du code : JavaScript (javascript)

Et le tour est joué. Rien qu’avec ce code très simple, on peut imaginer des applications intéressantes.

Petite précision : il est évidemment possible de créer plusieurs écouteurs d’événements (des “event listeners” en bon anglais) sur la même source d’événements, chacun pour traiter les messages envoyés sur un canal différent.

Dans mon prochain article, j’expliquerai comment utiliser concrètement les SSE. Parce qu’en vrai, les événements envoyés par le serveur ne sont souvent qu’une partie de la solution technique à mettre en œuvre.

1 commentaire pour “Les server-sent events (SSE)

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Notifiez-moi des commentaires à venir via email. Vous pouvez aussi vous abonner sans commenter.