HTML5 – WebSockets

Un téléphone

Tiens… « Sockets » et « HTML5 » dans le même titre… c’est curieux !

Non ami lecteur je ne me suis pas fourvoyé, la spécification HTML5 nous offre la possibilité de mettre en place des connexions bidirectionnelles persistantes entre le client et le serveur ! Si !

A nous le – véritable – mode connecté, les données qui arrivent vite, mais surtout quand il le faut, à l’initiative du client comme du serveur ! A nous les jeux !!

Ami lecteur je te propose une initiation à l’usage des WebSockets de HTML5, au travers d’un exemple facile à reproduire.

Du dynamisme avant tout !

Aujourd’hui un très grand nombre de sites Internet proposent du contenu dynamique et interactif.

Certains sites, que nous n’appelons plus « des sites » mais plutôt « des applications web », ont tant travaillé sur l’ergonomie que nous pourrions penser utiliser une application de bureau. Un exemple : Google Documents. Cet ensemble d’outils d’édition de documents ressemble beaucoup à une suite Office ou LibreOffice, n’est-ce pas ? (Bon, la version 2003, mais quand même.)

Tout cela pour dire que les pages Internet ont un besoin important de rafraîchir leurs contenus, d’effectuer des opérations tacites et d’échanger avec les serveurs ou d’autres clients, dans l’ombre.

Mais alors comment font les développeurs pour effectuer tout ceci sans jamais recharger nos pages, sans jamais geler l’écran ?

Sans WebSockets

Les développeurs n’ont pas attendu les WebSockets pour concevoir des pages dynamiques. Les solutions actuelles se basent sur des iframes, qui permettent de créer des « pages dans la page », ou sur Ajax et l’objet Javascript XMLHttpRequest.

Ajax est un acronyme qui signifie Asynchronous Javascript and XML. C’est une architecture informatique qui permet la conception d’applications Internet dynamiques. Une application dynamique est capable de travailler « derrière » la fenêtre du navigateur. Elle est capable de mettre à jour du contenu, déclencher des sauvegardes, etc., sans perturber la navigation de l’internaute.
L’objet  XMLHttpRequest est l’élément Javascript en charge de ces échanges asynchrones.

Ces techniques sont souvent utilisées comme des artifices, compte-tenu du besoin qu’elles satisfont. Rappelons qu’un serveur web ne prend pas l’initiative d’un échange avec le navigateur de l’internaute. Quand bien même il aurait un contenu frais ou le résultat d’une opération à lui transmettre, il doit attendre d’être sollicité par le biais d’une requête (asynchrone ou non), et c’est en y répondant qu’il transmet ses informations.

Pour simuler un échange en temps réel les codes embarqués dans le navigateur client sont alors obligés d’interroger périodiquement le serveur. Ces demandes fréquentes impliquent toutes la mise en place d’une nouvelle connexion, l’échange, l’interprétation des données s’il y en a, puis la fermeture de la connexion.

La technique est coûteuse, et se révèle inadaptée pour des échanges intensifs de données comme en nécessitent les jeux en réseau, les travaux collaboratifs, les outils de discussion instantanée, etc.

Attention toutefois, les WebSockets ne sont pas là pour supplanter Ajax. Cette nouvelle API répond simplement à un besoin qui n’était jusqu’à présent pas adressé.

Parce que nous le valons bien, un deuxième point culture : API est un sigle anglais qui signifie Application Programming Interface. Il s’agit d’un ensemble de méthodes, d’objets et de constantes qui permet de travailler sur un point précis. Ici les échanges réseaux.

Avec WebSockets

Les WebSockets sont donc la solution au problème évoqué au point précédent. Mais qu’est-ce qu’un WebSocket ? C’est l’adaptation du très classique socket, élément indispensable de la programmation réseau, directement accessible depuis le navigateur.

Mais qu’est-ce qu’un socket entends-je encore ?! C’est le point de sortie du programme informatique vers les ressources réseau de son ordinateur (la carte réseau, le câble, le monde extérieur…), ou vers d’autres processus du même ordinateur. Ordinairement on utilise des sockets depuis des applications conçues en langages « bas niveau » comme les applications conçues en C. Beaucoup de langages « haut niveau » proposent une couche d’abstraction qui simplifie leur usage, mais au bout du bout il s’agit bien de sockets.

Enfin et parce que tu l’as compris, je suis puriste à mes heures, j’ai cherché un équivalent français de socket, mais n’en ai pas trouvé. Nous pouvons considérer qu’un socket est un canal de communication bidirectionnel connecté en permanence, aussi je parlerai de canal et de canaux.

Le WebSocket permet donc d’ouvrir un canal de communication entre le navigateur de l’internaute et le serveur web. Le protocole utilisé est TCP (pour Transmission Control Protocol). On parle donc de mode connecté, puisque le protocole TCP impose aux deux parties de se présenter l’une à l’autre avant d’échanger les premières informations (c’est ce qu’on appelle la « poignée de main »). Une fois la connexion établie le navigateur comme le serveur peuvent envoyer des informations. De la même manière la fin de la connexion est initiée par l’une ou l’autre des parties.

Attention pas de méprise ! HTTP qui est le protocole standard utilisé pour les requêtes synchrones et asynchrones utilise aussi TCP, au dessus duquel il se situe. Une requête HTTP ouvre donc une connexion TCP, mais la clôt une fois la réponse reçue.
L’avantage du WebSocket est bien que cette connexion reste ouverte et sous votre contrôle.

Une fois que le canal est ouvert entre un navigateur et un serveur, de nombreux événements Javascript peuvent être capturés et permettent au client de réagir en temps réel.

Lançons-nous !

Nous allons fabriquer un exemple concret, une ébauche de chat. Pour ne pas aborder trop de nouveautés à la fois le tutoriel qui suit ne traite pas du code serveur permettant de gérer les informations échangées. Si ça t’intéresse, beaucoup de ressources traitent de ce sujet sur Internet. Il existe notamment le projet phpwebsocket qui est facile à exploiter.

Pour notre besoin nous utiliserons le service en ligne WebSocket.org qui fournit un serveur basique. Ce dernier renvoie les informations qu’il reçoit. Simple.

Création d’un objet WebSocket

if(!window.WebSocket)
	throw "Impossible d'utiliser WebSocket. Votre navigateur ne connait pas cette classe.";

var ws = new WebSocket("ws://echo.websocket.org");

La construction de l’objet WebSocket provoquera une tentative de connexion au serveur. Si la connexion est établie l’évènement open sera levé. Si la connexion échoue alors error sera levé, ainsi que close. Il est possible de consulter l’état de la connexion à n’importe quel moment grâce à la propriété readyState.

Valeurs de readyState Signification
WebSocket.CONNECTING
La tentative de connexion est en cours.
WebSocket.OPEN
La connexion est établie.
WebSocket.CLOSING
La tentative de déconnexion est en cours.
WebSocket.CLOSED
Le canal est fermé.

Nous définissons immédiatement le comportement du canal :

ws.onopen = function()
{
	Log(ReadyState(ws.readyState), "websocketstatus");
}

ws.onclose = function(e)
{
	Log(ReadyState(ws.readyState), "websocketstatus");
	if(e.wasClean)
	{
		Log("Connexion proprement terminée.", "websocketstatus");
	}
	else
	{
		Log(e.reason, "websocketerror");
		Log("Connexion terminée.", "websocketstatus");
	}
	ws = null;
}

ws.onerror = function(e)
{
	Log("Une erreur est survenue.", "websocketerror");
}

ws.onmessage = function(e)
{
	Log("reçu  > " + e.data, "websocketmsg");
}

La fonction Log() informe l’utilisateur. La fonction ReadyState() fait le parallèle entre le code de readyState et un texte intelligible. Ces deux fonctions ne font pas partie de l’API WebSockets.

L’objet reçu avec l’évènement close nous permet de savoir comment s’est déroulé la fermeture du canal. L’attribut wasClean est un booléen tandis que reason est une chaîne de caractères contenant une information intelligible. Enfin code que je n’utilise pas ici fournit un code retour relatif aux conditions de déconnexion.

Notre canal est ouvert ! Nous lui avons donné un comportement très simple : tout ce qu’il fait est signalé à l’utilisateur.

Envoi d’un message

Le canal possède une méthode send() permettant d’envoyer des données. Il est possible d’envoyer plusieurs types de données : les chaînes de caractères et les format plus complexes ArrayBuffer et  Blob. Dans notre exemple nous n’enverrons que des chaînes de caractères. C’est cette possibilité qui est la mieux supportée par les navigateurs, et c’est la seule que l’auteur de cet article comprend bien.

function Send()
{
	var f = document.forms["send_message"];
	ws.send(f.msg.value);
	Log("envoyé> " + f.msg.value, "websocketmsg");
}

Le texte envoyé est récupéré sur un champ de saisie présent sur la page (l’élément input avec  type=”text”).

Si tu es habitué à utiliser les sockets en C tu peux constater comme moi que leur usage en Javascript est considérablement simplifié. En effet c’est tout pour l’envoi de données !

Notre projet fait un usage très modeste de son objet WebSocket. Nous pouvons aisément imaginer qu’une application échangeant d’importants volumes de données voudra suivre de près l’avancement des opérations.
Entre autres propriétés avancées WebSocket nous permet de savoir combien d’octets de données sont en attente du grand départ vers le monde extérieur. Il s’agit de la propriété bufferedAmount.
Un développeur avisé consultera cet attribut pour éviter de noyer inutilement sa carte réseau sous des quantités trop importantes de données à envoyer ! *clin d’oeil de connivence*

Réception d’un message

Lorsque le serveur répond l’évènement message est levé, et nous avons déjà défini un comportement à adopter : informer l’utilisateur.

Conclusion

L’API WebSockets ouvre beaucoup de possibilités au développeur web et il va de soi que notre exemple ne met pas en lumière tout le potentiel de cette technologie.

Beaucoup de nouvelles choses vont être imaginées et le sont déjà. Je veux pour preuve les nombreux exemples figurant sur cette page. Qui sait, c’est peut-être toi qui feras le prochain jeu à-la-mode-et-super-addictif ?

Les débuts de l’API WebSockets ont été quelques peu freinés par la mise en lumière de problèmes de sécurité au niveau du protocole sous-jacent. Aujourd’hui les navigateurs supportent mieux l’interface, Google Chrome et Mozilla Firefox en tête !

Le projet complet de démonstration est disponible sur GitHub : SoWebSockets. Pour tester en live ca se passe sur JSFiddle !

Nombre de vue : 102

COMMENTAIRES 4 commentaires

  1. […] tout en tableau avec le bootstrap table. J’aurais souhaité faire l’inverse avec les WebSockets, c’est à dire le serveur (ici l’ESP) poussant les données vers le navigateur mais je […]

  2. […] tout en tableau avec le bootstrap table. J’aurais souhaité faire l’inverse avec les WebSockets, c’est à dire le serveur (ici l’ESP) poussant les données vers le navigateur mais je […]

  3. khaled dit :

    waw très bien expliqué merci l’amis Merci infiniment pour ce tuto de ouuuuuufffff

  4. Très bon post. Juste un détail; toutes les sockets ne sont pas TCP….certaines sont en UDP et c’est même préférable pour une meilleure réactivité.
    …oui, alors on peut perdre un message.
    Je ne sais pas si le websocket te permet de choisir entre TCP et UDP !
    Enfin, ce n’est pas du HTML5….déclaratif, c’est du Javascript !
    Il eut été plus malin de faire aussi une API en HTML, totalement déclarative (comme qui gère une requête post) et mise en place coté serveur.
    Il aurait été utile aussi qu’AJAX soit plus encapsulé dans HTML….mais on ne change pas un standard facilement.

    Le seul truc bête avec les websockets, c’est que la mise en œuvre se fasse coté client, en Javascript. Puisque que pour ajouter une fonctionnalité au serveur (de réveiller des clients), pourquoi la proposer en Javascript coté client….c’est débile ?
    D’autant qu’en imposant Javascript, on limite beaucoup l’expression déclarative d’HTML….or c’était bien le but initial de ce language !
    Manquerait plus que Javascript propose un langage de description de ressources et HTML ne servirait plus à rien.

AJOUTER UN COMMENTAIRE