Les WebSockets dans Windows Consumer Preview

Dans Windows 8 Consumer Preview et Server Bêta, IE10 et toutes les autres fonctionnalités du client et du serveur Microsoft WebSocket prennent maintenant en charge la version finale du protocole WebSocket de l'IETF. IE10 implémente en outre la recommandation de l'API WebSocket du W3C.

Les WebSockets sont stables et prêts à être utilisés par les développeurs qui veulent créer des applications et des services innovants. Ce billet présente simplement l'API WebSocket du W3C et son protocole WebSocket sous-jacent. La nouvelle démonstration Flipbook utilise la dernière version de l'API et du protocole.

Dans mon billet précédent, j'ai présenté les scénarios WebSocket :

Les WebSockets permettent aux applications Web de fournir des notifications et des mises à jour en temps réel au navigateur. Les développeurs ont rencontré des problèmes pour contourner les limitations du modèle requête-réponse HTTP d'origine du navigateur, qui n'était pas conçu pour les scénarios en temps réel. Les WebSockets permettent aux navigateurs d'ouvrir un canal de communication bidirectionnel en duplex intégral avec les services. Ce canal peut alors être utilisé pour envoyer des données de part et d'autre. Maintenant, les sites (des réseaux sociaux et des jeux aux sites financiers) offrent de meilleurs scénarios en temps réel, dans l'idéal en utilisant les mêmes balises sur différents navigateurs.

Depuis ce billet de septembre 2011, les groupes de travail ont réalisé des progrès significatifs. Le protocole WebSocket est maintenant un protocole normalisé proposé par l'IETF. De plus, l'API WebSocket du W3C est une recommandation du W3C.

Introduction de l'API WebSocket à l'aide d'un serveur echo d'exemple

Les extraits de code ci-dessous utilisent un simple serveur echo créé avec l'espace de noms System.Web.WebSockets d'ASP.NET pour renvoyer le texte et les messages binaires qui sont envoyés par l'application. L'application permet à l'utilisateur de saisir du texte à envoyer et à renvoyer sous forme de message texte ou à dessiner une image pouvant être envoyée et renvoyée sous forme de message binaire.

Capture d'écran illustrant l'application d'exemple echo des WebSockets.

Si vous souhaitez un exemple plus complexe qui vous permet d'expérimenter les différences en termes de latence et de performances entre les WebSockets et l'interrogation HTTP, consultez la démonstration Flipbook.

Détails de la connexion à un serveur WebSocket

Cette explication simple est fondée sur une connexion directe entre l'application et le serveur. Si un proxy est configuré, IE10 démarre le processus en envoyant une requête HTTP CONNECT au proxy.

Lorsqu'un objet WebSocket est créé, une poignée de main est échangée entre le client et le serveur pour établir la connexion WebSocket.

Diagramme illustrant une demande de mise à niveau HTTP GET d'un client HTTP à un serveur HTTP.

IE10 démarre le processus en envoyant une requête HTTP au serveur :

GET /echo HTTP/1.1

Host: example.microsoft.com

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

Origin: https://microsoft.com

Sec-WebSocket-Version: 13

Examinons chaque partie de cette requête.

Le processus de connexion démarre par une requête HTTP GET standard qui permet à la requête de traverser les pare-feux, les proxies et tous les autres intermédiaires :

GET /echo HTTP/1.1

Host: example.microsoft.com

L'en-tête Upgrade HTTP demande que le serveur modifie le protocole application-couche de HTTP en protocole WebSocket.

Upgrade: websocket

Connection: Upgrade

Le serveur transforme la valeur de l'en-tête Sec-WebSocket-Key dans sa réponse pour démontrer qu'il a compris le protocole WebSocket :

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

L'en-tête Origin est défini par IE10 de façon à autoriser le serveur à appliquer la sécurité d'origine.

Origin: https://microsoft.com

L'en-tête Sec-WebSocket-Version identifie la version demandée du protocole. La version 13 est la version finale dans la norme proposée par l'IETF :

Sec-WebSocket-Version: 13

Si le serveur accepte la demande de mise à niveau du protocole application-couche, il renvoie une réponse HTTP 101 Basculement de protocoles :

Diagramme illustrant une réponse HTTP 101 Basculement de protocoles d'un serveur HTTP à un client HTTP.

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Pour démontrer qu'il a compris le protocole WebSocket, le serveur effectue une transformation normalisée sur l'en-tête Sec-WebSocket-Key à partir de la requête du client et renvoie les résultats dans l'en-tête Sec-WebSocket-Accept :

Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

IE10 compare ensuite Sec-WebSocket-Key avec Sec-WebSocket-Accept pour vérifier que le serveur est un serveur WebSocket, et non un serveur HTTP à l'ambition démesurée.

La poignée de main du client a établi une connexion « HTTP sur TCP » entre IE10 et le serveur. Une fois que le serveur a renvoyé sa réponse 101, le protocole application-couche bascule de HTTP aux WebSockets, qui utilisent la connexion TCP précédemment établie.

À ce stade, HTTP a entièrement quitté la scène. À l'aide du protocole filaire WebSocket léger, les messages peuvent maintenant être envoyés ou reçus par l'une ou l'autre extrémité à tout moment.

Diagramme illustrant l'envoi de messages WebSocket entre deux sockets Web.

Programmation de la connexion à un serveur WebSocket

Le protocole WebSocket définit deux nouveaux schémas URI qui sont similaires aux schémas HTTP.

  • "ws:" "//" host [ ":" port ] path [ "?" query ] a pour modèle le schéma « http: ». Son port par défaut est 80. Il est utilisé pour les connexions non sécurisées (non chiffrées).
  • "wss:" "//" host [ ":" port ] path [ "?" query ] a pour modèle le schéma « https: ». Son port par défaut est 443. Il est utilisé pour les connexions sécurisées « tunnelées » sur le protocole TLS (Transport Layer Security).

Lorsque des proxies ou des intermédiaires réseaux sont présents, il est fort probable que les connexions sécurisées réussiront, car les intermédiaires sont moins enclins à tenter de transformer le trafic sécurisé.

L'extrait de code suivant établit une connexion WebSocket :

var host = "ws://example.microsoft.com";

var socket = new WebSocket(host);

ReadyState – À vos marques … Prêt … Partez …

L'attribut WebSocket.readyState représente l'état de la connexion : [CONNECTING] (CONNEXION EN COURS), [OPEN] (OUVERTE), [CLOSING] (FERMETURE EN COURS) ou [CLOSED] (FERMÉE). Lors de la création du WebSocket, l'attribut readyState est défini sur CONNECTING. Lorsque la connexion est établie, l'attribut readyState est défini sur OPEN. Si la connexion ne parvient pas à s'établir, l'attribut readyState est défini sur CLOSED.

Inscription aux événements d'ouverture

Pour recevoir des notifications lorsque la connexion a été créée, l'application doit s'inscrire aux événements d'ouverture.

socket.onopen = function (openEvent) {

document.getElementById("serverStatus").innerHTML = 'Web Socket State::' + 'OPEN';

};

Détails sous-jacents à l'envoi et à la réception de messages

Après une poignée de main réussie, l'application et le serveur Websocket peuvent échanger des messages WebSocket. Un message se compose d'une séquence d'un ou de plusieurs fragments de messages ou « blocs » de données.

Chaque bloc comprend des informations du type :

  • Longueur du bloc
  • Type de message (binaire ou texte) dans le premier bloc du message
  • Indicateur (FIN) indiquant s'il s'agit du dernier bloc du message

Diagramme illustrant le contenu des blocs WebSocket.

IE10 rassemble les blocs en un message complet avant de le transmettre au script.

Programmation de l'envoi et de la réception de messages

L'API send permet aux applications d'envoyer des messages à un serveur Websocket sous forme de texte UTF-8 ou de types de données ArrayBuffer ou Blob.

Par exemple, cet extrait de code récupère le texte entré par l'utilisateur et l'envoie au serveur sous forme de message texte au format UTF-8, pour qu'il soit renvoyé. Il vérifie que l'attribut readyState du Websocket est défini sur OPEN :

function sendTextMessage() {

if (socket.readyState != WebSocket.OPEN)

return;

 

var e = document.getElementById("textmessage");

socket.send(e.value);

}

Cet extrait de code récupère l'image dessinée par l'utilisateur dans la zone de dessin et l'envoie au serveur sous forme de message binaire :

function sendBinaryMessage() {

if (socket.readyState != WebSocket.OPEN)

return;

 

var sourceCanvas = document.getElementById('source');

// msToBlob returns a blob object from a canvas image or drawing

socket.send(sourceCanvas.msToBlob());

// ...

}

Inscription aux événements des messages

Pour recevoir des messages, l'application doit s'inscrire aux événements des messages. Le gestionnaire d'événements reçoit un attribut MessageEvent qui contient les données de MessageEvent.data. Les données peuvent être reçues sous forme de messages texte ou binaires.

Lorsqu'un message binaire est reçu, l'attribut WebSocket.binaryType contrôle si les données du message sont renvoyées sous forme de type de données Blob ou ArrayBuffer. L'attribut peut être défini sur « blob » ou « arraybuffer ».

Les exemples ci-dessous utilisent la valeur par défaut « blob ».

Cet extrait de code reçoit l'image ou le texte renvoyé par le serveur WebSocket. Si les données sont de type Blob, alors une image a été renvoyée et est dessinée dans la zone de dessin de destination. Sinon, un message texte au format UTF-8 a été renvoyé et s'affiche dans un champ de texte.

socket.onmessage = function (messageEvent) {

if (messageEvent.data instanceof Blob) {

var destinationCanvas = document.getElementById('destination');

var destinationContext = destinationCanvas.getContext('2d');

var image = new Image();

image.onload = function () {

destinationContext.clearRect(0, 0, destinationCanvas.width, destinationCanvas.height);

destinationContext.drawImage(image, 0, 0);

}

image.src = URL.createObjectURL(messageEvent.data);

} else {

document.getElementById("textresponse").value = messageEvent.data;

}

};

Détails de la fermeture d'une connexion WebSocket

Tout comme il existe une poignée de main d'ouverture, il existe une poignée de main de fermeture. L'une ou l'autre extrémité (l'application ou le serveur) peut initier cette poignée de main.

Un type de bloc spécial (un bloc de fermeture) est envoyé à l'autre extrémité. Le bloc de fermeture peut contenir un code d'état facultatif et la raison de la fermeture. Le protocole définit un jeu de valeurs appropriées pour le code d'état. L'expéditeur du bloc de fermeture ne doit pas envoyer d'autres données d'application après le bloc de fermeture.

Lorsque l'autre extrémité reçoit le bloc de fermeture, il répond avec son propre bloc de fermeture. Il peut envoyer les messages en attente avant de répondre avec le bloc de fermeture.

Diagramme illustrant le message du bloc de fermeture et la réponse.

Programmation de la fermeture d'un WebSocket et inscription aux événements de fermeture

L'application initie la poignée de main de fermeture sur une connexion ouverte avec l'API close  :

socket.close(1000, "normal close");

Pour recevoir des notifications lorsque la connexion a été fermée, l'application doit s'inscrire aux événements de fermeture.

socket.onclose = function (closeEvent) {

document.getElementById("serverStatus").innerHTML = 'Web Socket State::' + 'CLOSED';

};

L'API close accepte deux paramètres facultatifs : un code d'état tel que défini par le protocole et une description. Le code d'état doit être de 1 000 ou se situer entre 3 000 et 4 999. Lors de la fermeture, l'attribut readyState est défini sur CLOSING. Après qu'IE10 a reçu la réponse de fermeture du serveur, l'attribut readyState est défini sur CLOSED et un événement de fermeture est déclenché.

Utilisation de Fiddler pour voir le trafic des WebSockets

Fiddler est un proxy de débogage HTTP connu. Des avancées en matière de prise en charge du protocole WebSocket ont été faites dans les dernières versions. Vous pouvez examiner les en-têtes échangés lors de la poignée de main WebSocket :

Capture d'écran de Fiddler montrant une requête WebSocket.

Tous les messages WebSocket sont également consignés. Dans la capture d'écran ci-dessous, vous pouvez voir que « spiral » a été envoyé au serveur sous forme de message texte UTF-8 et renvoyé :

Capture d'écran de Fiddler montrant une réponse WebSocket.

Conclusion

Si vous souhaitez en savoir plus sur les WebSockets, vous pouvez regarder ces sessions tirées de la conférence Microsoft //Build/ de septembre 2011 :

Si vous désirez en savoir plus sur les technologies Microsoft permettant de créer un service WebSocket, vous pouvez lire ces billets :

Je vous encourage à développer en utilisant les WebSockets dès aujourd'hui et à nous faire part de vos commentaires.

—Brian Raymor, chef de projet, Windows Networking