Présentation de WebSockets : sockets sur le Web

Le problème: une faible latence des connexions client-serveur et serveur-client

Le Web a été largement construit autour du soi-disant paradigme requête/réponse du protocole HTTP. Un client charge une page Web et rien ne se passe tant que l'utilisateur n'a pas cliqué sur la page suivante. Vers 2005, AJAX a commencé à rendre le Web plus dynamique. Malgré tout, toute communication HTTP était pilotée par le client, ce qui nécessitait une interaction avec l'utilisateur ou une interrogation périodique pour charger de nouvelles données à partir du serveur.

Les technologies qui permettent au serveur d'envoyer des données au client au moment précis où il sait que de nouvelles données sont disponibles existent depuis un certain temps. Elles sont désignées par des noms tels que "Push" ou "Comet". L'une des méthodes les plus courantes permettant de créer l'illusion d'une connexion initiée par un serveur est appelée "Long polling". Avec un long sondage, le client ouvre une connexion HTTP avec le serveur qui le maintient ouvert jusqu'à l'envoi de la réponse. Chaque fois que le serveur dispose de nouvelles données, il envoie sa réponse (d'autres techniques impliquent le Flash, les requêtes XHR multipart et donc les fichiers HTML). Les longues sondages et les autres techniques fonctionnent très bien. Vous les utilisez tous les jours dans des applications telles que le chat Gmail.

Cependant, toutes ces solutions de contournement ont un problème: elles entraînent la surcharge du HTTP, ce qui les rend pas adaptées aux applications à faible latence. Il peut s'agir de jeux multijoueurs de tir en vue subjective dans un navigateur ou de tout autre jeu en ligne incluant un composant en temps réel.

Présentation de WebSocket: déployer des sockets sur le Web

La spécification WebSocket définit une API qui établit des connexions de type "socket" entre un navigateur Web et un serveur. En d'autres termes, il existe une connexion persistante entre le client et le serveur, et les deux parties peuvent commencer à envoyer des données à tout moment.

Getting Started

Vous ouvrez une connexion WebSocket en appelant simplement le constructeur WebSocket:

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

Notez la présence de ws:. Il s'agit du nouveau schéma d'URL pour les connexions WebSocket. Il existe également wss: pour une connexion WebSocket sécurisée, de la même manière que https: est utilisé pour les connexions HTTP sécurisées.

Le fait d'associer immédiatement certains gestionnaires d'événements à la connexion vous permet de savoir quand celle-ci est ouverte, quand elle reçoit des messages entrants ou quand une erreur s'est produite.

Le deuxième argument accepte des sous-protocoles facultatifs. Il peut s'agir d'une chaîne ou d'un tableau de chaînes. Chaque chaîne doit représenter un nom de sous-protocole. Le serveur n'accepte qu'un seul des sous-protocoles transmis dans le tableau. Vous pouvez déterminer les sous-protocoles acceptés en accédant à la propriété protocol de l'objet WebSocket.

Les noms de sous-protocoles doivent correspondre à l'un des noms de sous-protocoles enregistrés dans le registre de l'IANA. Il n'existe actuellement qu'un seul nom de sous-protocole (savons) en février 2012.

// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};

Communiquer avec le serveur

Dès qu'une connexion au serveur est établie (lors du déclenchement de l'événement open), nous pouvons commencer à envoyer des données au serveur à l'aide de la méthode send('your message') sur l'objet de connexion. Auparavant, il ne prenait en charge que les chaînes, mais dans les dernières spécifications, il peut désormais également envoyer des messages binaires. Pour envoyer des données binaires, vous pouvez utiliser un objet Blob ou ArrayBuffer.

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

De même, le serveur peut nous envoyer des messages à tout moment. Chaque fois que cela se produit, le rappel onmessage est déclenché. Le rappel reçoit un objet d'événement, et le message réel est accessible via la propriété data.

WebSocket peut également recevoir des messages binaires dans la dernière spécification. Les trames binaires peuvent être reçues au format Blob ou ArrayBuffer. Pour spécifier le format du binaire reçu, définissez la propriété binaryType de l'objet WebSocket sur "blob" ou "arraybuffer". Le format par défaut est "blob". (Vous n'avez pas besoin d'aligner le paramètre binaryType lors de l'envoi.)

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};

Une autre fonctionnalité récemment ajoutée de WebSocket est les extensions. À l'aide des extensions, il sera possible d'envoyer des frames compressés, multiplexés, etc. Vous trouverez les extensions acceptées par le serveur en examinant la propriété extensions de l'objet WebSocket après l'événement d'ouverture. Aucune spécification d'extension officielle n'a encore été publiée en février 2012.

// Determining accepted extensions
console.log(connection.extensions);

Communication multi-origine

En tant que protocole moderne, la communication multi-origine est intégrée à WebSocket. Même si vous devez toujours vous assurer de ne communiquer qu'avec des clients et des serveurs de confiance, WebSocket permet la communication entre les parties sur n'importe quel domaine. Le serveur décide si son service doit être mis à la disposition de tous les clients ou uniquement de ceux qui résident sur un ensemble de domaines bien définis.

Serveurs proxy

Chaque nouvelle technologie s'accompagne d'un nouvel ensemble de problèmes. Dans le cas de WebSocket, il s'agit de la compatibilité avec les serveurs proxy qui assurent la médiation des connexions HTTP dans la plupart des réseaux d'entreprise. Le protocole WebSocket utilise le système de mise à niveau HTTP (normalement utilisé pour HTTP/SSL) pour "mettre à niveau" une connexion HTTP vers une connexion WebSocket. Certains serveurs proxy n'apprécient pas cette fonctionnalité et perdront leur connexion. Ainsi, même si un client donné utilise le protocole WebSocket, il peut ne pas être possible d'établir une connexion. Cela rend la section suivante encore plus importante :)

Utiliser WebSockets aujourd'hui

Il s'agit d'une technologie récente qui n'est pas complètement implémentée dans tous les navigateurs. Toutefois, vous pouvez utiliser WebSocket aujourd'hui avec les bibliothèques qui utilisent l'une des solutions de secours mentionnées ci-dessus lorsque WebSocket n'est pas disponible. La bibliothèque socket.io est devenue très populaire dans ce domaine. Elle est fournie avec une implémentation client et serveur du protocole, et inclut des alternatives (socket.io n'est pas encore compatible avec la messagerie binaire depuis février 2012). Il existe également des solutions commerciales telles que PusherApp, qui peuvent être facilement intégrées à n'importe quel environnement Web en fournissant une API HTTP pour envoyer des messages WebSocket aux clients. En raison de la requête HTTP supplémentaire, il y aura toujours une surcharge supplémentaire par rapport au WebSocket pur.

Côté serveur

L'utilisation de WebSocket crée un tout nouveau modèle d'utilisation pour les applications côté serveur. Bien que les piles de serveurs traditionnelles telles que LAMP soient conçues autour du cycle requête/réponse HTTP, elles ne gèrent souvent pas bien un grand nombre de connexions WebSocket ouvertes. Pour maintenir un grand nombre de connexions ouvertes en même temps, vous devez disposer d'une architecture offrant une simultanéité élevée pour un coût en termes de performances faible. Ces architectures sont généralement conçues autour de threads ou d'E/S non bloquantes.

Implémentations côté serveur

Versions du protocole

Le protocole filaire (un handshake et le transfert de données entre le client et le serveur) pour WebSocket est désormais RFC6455. Les derniers Chrome et Chrome pour Android sont entièrement compatibles avec la norme RFC6455, y compris la messagerie binaire. Firefox sera compatible avec la version 11 et Internet Explorer sur la version 10. Vous pouvez toujours utiliser d'anciennes versions du protocole, mais cela n'est pas recommandé, car elles sont connues pour être vulnérables. Si vous disposez d'implémentations de serveur pour d'anciennes versions du protocole WebSocket, nous vous recommandons de les mettre à niveau vers la dernière version.

Cas d'utilisation

Utilisez WebSocket chaque fois que vous avez besoin d'une connexion quasiment en temps réel à faible latence entre le client et le serveur. Gardez à l'esprit que cela peut impliquer de repenser la façon dont vous créez vos applications côté serveur en vous concentrant sur les technologies telles que les files d'attente d'événements. Voici quelques exemples de cas d'utilisation:

  • Jeux multijoueurs en ligne
  • Applications de chat
  • Tchéquier d'événements sportifs en direct
  • Mise à jour en temps réel des flux sociaux

Démonstrations

Références