Ecrire des applications client WebSocket

Les WebSockets représentent une technologie, basée sur le protocole web socket, qui permet d'établir une session de communication bilatérale entre un navigateur web et un serveur. Un navigateur web est un exemple typique de client websocket typique mais le protocole n'est dépendant d'aucune plateforme.

Note : Un exemple d'utilisation des WebSockets à travers un système de chat sera mis à disposition sous forme de code dès que nos infrastructures seront en mesure de supporter les WebSockets.

Note: Cette fonctionnalité est disponible via les Web Workers.

Création d'un objet WebSocket

Pour utiliser le protocole WebSocket, il faut créer un objet WebSocket ; celui-ci essaiera automatiquement d'ouvrir une connexion avec le server.

Le constructeur WebSocket accepte un paramètre obligatoire et un paramètre optionnel :

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString protocols
);

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString[] protocols
);
url

L'URL à laquelle le client se connecte, et le serveur répond.

protocols Facultatif

Soit une chaîne décrivant un protocole unique, soit une liste de chaînes décrivant chacune un protocole. Ces chaînes permettent d'indiquer des sous-protocoles, de façon à ce qu'un serveur puisse implémenter plusieurs sous-protocoles WebSocket (par example, on pourrait vouloir qu'un serveur soit capable de traiter différents types d'interactions en fonction du protocole spécifié). Si aucun protocole n'est spécifié, l'argument prend la valeur d'une chaîne vide.

Le constructeur peut renvoyer des exceptions:

SECURITY_ERR

Le port sur lequel on essaie d'établir la connexion est bloqué.

Erreurs de connexion

Si une erreur se produit lors de la tentative de connexion, un évènement nommé "error" est d'abord renvoyé à l'objet WebSocket (invoquant ainsi son gestionnaire d'évènement onerror) suivi d'un évènement CloseEvent (qui invoque alors son gestionnaire d'évènement onclose) indiquant la raison de la clôture.

A partir de Firefox 11, un message d'erreur descriptif est envoyé à la console de la plateforme Mozilla, et un code de fermeture tel que défini dans la RFC 6455, Section 7.4 est envoyé à travers l'évènement CloseEvent.

Exemples

Cet exemple simple crée un nouvel objet WebSocket, qui se connecte au serveur à l'adresse ws://www.example.com/socketserver. Un protocole spécifique "protocolOne" est indiqué dans cette exemple, bien qu'il ne soit pas obligatoire.

js
var exampleSocket = new WebSocket(
  "ws://www.example.com/socketserver",
  "protocolOne",
);

Lorsque la connexion est établie, la propriété readyState de l'objet exampleSocket prend la valeur CONNECTING. Sa valeur devient OPEN une fois que la connexion est prête à transférer des données.

Pour ouvrir une connexion flexible quant aux protocoles supportés, on spécifie une liste de protocoles:

js
var exampleSocket = new WebSocket("ws://www.example.com/socketserver", [
  "protocolOne",
  "protocolTwo",
]);

Une fois la connexion établie (c'est-à-dire quand readyState a la valeur OPEN), la propriété protocol indique quel protocole le server a sélectionné.

Dans les exemples ci-dessus on a remplacé http par ws, et de la même façon on peut remplacer https par wss . L'établissement d'une connexion WebSocket repose sur le méchanisme HTTP Upgrade, donc la requête pour l'upgrade de protocole est implicite lorsqu'on s'adresse au server HTTP avec ws://www.example.com ou wss://www.example.com.

Envoi de données vers le serveur

Une fois la connexion ouverte on peut commencer à tranférer des données vers le serveur en appelant la méthode send() de l'objet WebSocket pour chaque message que l'on veut envoyer :

Les données peuvent être envoyées sous forme de chaîne Blob ou de ArrayBuffer.

Note : Avant la version 11, Firefox supportait l'envoi de données uniquement sous forme de chaîne.

Comme l'établissement d'une connexion est asynchrone, et peut potentiellemet échouer, appeler la méthode send() juste après la création d'un objet WebSocket peut ne pas fonctionner. Il est plus sûr de définir un gestionnaire d'évènement onopen, et de n'essayer d'envoyer des données que lorsqu'il est appelé.

js
exampleSocket.onopen = function (event) {
  exampleSocket.send(
    "Voici un texte que le serveur attend de recevoir dès que possible !",
  );
};

Utilisation de JSON pour transmettre des objets

Il peut être utile d'utiliser JSON pour envoyer des données complexes au serveur. Par exemple, un programme de chat peut interagir avec un serveur en utilisant un protocole qui implémente l'échange de paquets contenant des données encapsulées en JSON:

js
// Envoi d'un texte à tous les utilisateurs à travers le serveur
function sendText() {
  // Création d'un objet msg qui contient les données
  // dont le serveur a besoin pour traiter le message
  var msg = {
    type: "message",
    text: document.getElementById("text").value,
    id: clientID,
    date: Date.now(),
  };

  // Envoi de l'objet msg à travers une chaîne formatée en JSON
  exampleSocket.send(JSON.stringify(msg));

  // Efface le texte de l'élément input
  // afin de recevoir la prochaine ligne de texte
  // que l'utilisateur va saisir
  document.getElementById("text").value = "";
}

Réception de données du serveur

WebSockets est une API orientée évènement; lorsqu'elle reçoit un message, un évènement "message" est envoyé au gestionnaire d'évènement onmessage. Pour écouter les données reçues, on peut écrire quelque chose comme:

js
exampleSocket.onmessage = function (event) {
  console.log(event.data);
};

Réception et interprétation d'objets JSON

Considérons l'application de chat évoquée dans Utilisation de JSON pour transmettre des objets. Le client peut recevoir différents types de paquets de données, tels que:

  • établissement d'une liaison (handshaking)
  • message texte
  • mise à jour de la liste d'utilisateurs

Le code qui interprète ces messages entrants pourrait être:

js
exampleSocket.onmessage = function (event) {
  var f = document.getElementById("chatbox").contentDocument;
  var text = "";
  var msg = JSON.parse(event.data);
  var time = new Date(msg.date);
  var timeStr = time.toLocaleTimeString();

  switch (msg.type) {
    case "id":
      clientID = msg.id;
      setUsername();
      break;
    case "username":
      text =
        "<b>User <em>" +
        msg.name +
        "</em> signed in at " +
        timeStr +
        "</b><br>";
      break;
    case "message":
      text = "(" + timeStr + ") <b>" + msg.name + "</b>: " + msg.text + "<br>";
      break;
    case "rejectusername":
      text =
        "<b>Your username has been set to <em>" +
        msg.name +
        "</em> because the name you chose is in use.</b><br>";
      break;
    case "userlist":
      var ul = "";
      for (i = 0; i < msg.users.length; i++) {
        ul += msg.users[i] + "<br>";
      }
      document.getElementById("userlistbox").innerHTML = ul;
      break;
  }

  if (text.length) {
    f.write(text);
    document.getElementById("chatbox").contentWindow.scrollByPages(1);
  }
};

Ici nous utilisons JSON.parse() pour convertir l'objet JSON en l'objet original, avant de l'examiner et le traiter.

Encodage du texte

Le texte reçu à travers une connexion WebSocket est encodé au format UTF-8.

Avant Gecko 9.0, certains charactères spéciaux dans une chaîne UTF-8 provoquaient l'interruption de la connexion. Maintenant Gecko accepte ces caractères.

Fermeture de la connexion

Quand on n'a plus besoin de la connexion WebSocket, on appelle la méthode close() de l'objet WebSocket:

js
exampleSocket.close();

Il peut être utile de vérifier la valeur de l'attribut bufferedAmount avant de fermer la connexion, pour s'assurer qu'il ne reste pas des données qui n'ont pas été transmises.

Considérations de sécurité

Il est déconseillé d'utiliser les WebSockets dans un environnement mixte, c'est-à-dire qu'il ne faut pas établir de connexion Websocket non sécurisée depuis une page chargée en HTTPS, et inversement. Certains navigateurs l'interdisent explicitement, comme Firefox à partir de la version 8.