Présentation de WebSockets : sockets sur le Web

Problème: connexions client-serveur et serveur-client à faible latence

Le Web s'est largement construit autour du paradigme de requête/réponse du protocole HTTP. Un client charge une page Web, puis rien ne se passe avant que l'utilisateur ne clique sur la page suivante. Vers 2005, AJAX a commencé à rendre le Web plus dynamique. Cependant, toutes les communications HTTP étaient gérées par le client, ce qui nécessitait une interaction de l'utilisateur ou un échantillonnage périodique pour charger de nouvelles données à partir du serveur.

Les technologies permettant au serveur d'envoyer des données au client au moment même où il sait que de nouvelles données sont disponibles existent depuis un certain temps. Ils peuvent s'appeler "Push" ou "Comet", par exemple. L'une des astuces les plus courantes pour créer l'illusion d'une connexion initiée par le serveur est appelée "long polling". Avec le long polling, le client ouvre une connexion HTTP au serveur, qui la maintient ouverte jusqu'à l'envoi de la réponse. Chaque fois que le serveur dispose de nouvelles données, il envoie la réponse (d'autres techniques impliquent des requêtes Flash, XHR multipart ou htmlfiles). Le long polling et les autres techniques fonctionnent très bien. Vous les utilisez tous les jours dans des applications telles que le chat Gmail.

Toutefois, tous ces solutions de contournement partagent un problème : ils génèrent des frais généraux liés à HTTP, ce qui ne les rend pas adaptés aux applications à faible latence. Il peut s'agir de jeux de tir multijoueurs à la première personne dans un navigateur ou de tout autre jeu en ligne avec un composant en temps réel.

Présentation de WebSocket: introduire les sockets sur le Web

La spécification WebSocket définit une API qui établit des connexions "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.

Premiers pas

Pour ouvrir une connexion WebSocket, il vous suffit d'appeler le constructeur WebSocket :

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

Notez ws:. Il s'agit du nouveau schéma d'URL pour les connexions WebSocket. wss: est également utilisé pour les connexions WebSocket sécurisées, de la même manière que https: 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 la connexion est ouverte, quand il a reçu des messages entrants ou quand il y a une erreur.

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, et le serveur n'accepte qu'un seul des sous-protocoles transmis dans le tableau. Le sous-protocole accepté peut être déterminé 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. À l'heure actuelle, seul un nom de sous-protocole (soap) est enregistré depuis 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);
};

Communication avec le serveur

Dès que nous avons établi une connexion au serveur (lorsque l'événement open est déclenché), nous pouvons commencer à envoyer des données au serveur à l'aide de la méthode send('your message') sur l'objet de connexion. Il n'était auparavant compatible qu'avec les chaînes, mais la dernière spécification permet également d'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. Dans ce cas, le rappel onmessage se déclenche. 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 selon la spécification la plus récente. 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". (Il n'est pas nécessaire 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 à WebSocket est les extensions. Grâce aux extensions, vous pouvez envoyer des trames compressées, multiplexées, etc. Vous pouvez trouver les extensions acceptées par le serveur en examinant la propriété "extensions" de l'objet WebSocket après l'événement d'ouverture. À ce jour (février 2012), aucune spécification officielle n'a été publiée pour les extensions.

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

Communication multi-origine

Étant donné qu'il s'agit d'un protocole moderne, la communication inter-origine est intégrée à WebSocket. Même si vous devez toujours vous assurer de ne communiquer qu'avec les clients et les serveurs de confiance, WebSocket permet la communication entre les parties sur n'importe quel domaine. Le serveur décide de rendre son service disponible pour tous les clients ou seulement 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, c'est 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 en connexion WebSocket. Certains serveurs proxy n'apprécient pas cela et coupent la connexion. Par conséquent, même si un client donné utilise le protocole WebSocket, il est possible qu'il ne soit pas possible d'établir une connexion. Ce qui rend la section suivante encore plus importante :)

Utiliser WebSockets dès aujourd'hui

WebSocket est encore une technologie jeune et n'est pas entièrement implémentée dans tous les navigateurs. Toutefois, vous pouvez utiliser WebSocket dès aujourd'hui avec des bibliothèques qui utilisent l'une des solutions de remplacement mentionnées ci-dessus lorsque WebSocket n'est pas disponible. Une bibliothèque qui est devenue très populaire dans ce domaine est socket.io, qui est fournie avec une implémentation client et serveur du protocole et inclut des solutions de secours (socket.io n'est pas encore compatible avec la messagerie binaire en 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 des frais supplémentaires par rapport à 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 de 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 qui accepte un nombre élevé de connexions simultanées à un coût faible en termes de performances. 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 la RFC 6455. Les dernières versions de Chrome et de Chrome pour Android sont entièrement compatibles avec la norme RFC6455, y compris la messagerie binaire. Firefox sera également compatible avec la version 11, et Internet Explorer avec la version 10. Vous pouvez toujours utiliser les anciennes versions du protocole, mais nous vous déconseillons de le faire, car elles sont connues pour être vulnérables. Si vous avez des implémentations de serveur pour d'anciennes versions du protocole WebSocket, nous vous recommandons de passer à la dernière version.

Cas d'utilisation

Utilisez WebSocket chaque fois que vous avez besoin d'une connexion quasiment en temps réel et à faible latence entre le client et le serveur. N'oubliez pas que vous devrez peut-être 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 en ligne multijoueurs
  • Applications de chat
  • Fil d'actualités sportives en direct
  • Mise à jour en temps réel des flux sur les réseaux sociaux

Démonstrations

Références