Envoyer des données entre les navigateurs avec les canaux de données WebRTC

L'envoi de données entre deux navigateurs pour la communication, les jeux ou le transfert de fichiers peut être un processus assez complexe. Cela nécessite de configurer et de payer un serveur pour relayer les données, et peut-être de le faire évoluer vers plusieurs centres de données. Dans ce scénario, la latence peut être élevée et il est difficile de préserver la confidentialité des données.

Ces problèmes peuvent être atténués en utilisant l'API RTCDataChannel de WebRTC pour transférer des données directement d'un pair à un autre. Cet article explique comment configurer et utiliser les canaux de données, et présente les cas d'utilisation courants sur le Web aujourd'hui.

Pourquoi un autre canal de données ?

Nous avons WebSocket, AJAX et Server Sent Events. Pourquoi avons-nous besoin d'un autre canal de communication ? WebSocket est bidirectionnel, mais toutes ces technologies sont conçues pour la communication vers ou depuis un serveur.

RTCDataChannel adopte une approche différente :

  • Il fonctionne avec l'API RTCPeerConnection, qui permet la connectivité peer-to-peer. Cela peut réduire la latence (pas de serveur intermédiaire et moins de "sauts").
  • RTCDataChannel utilise le protocole SCTP (Stream Control Transmission Protocol), qui permet de configurer la sémantique de distribution, la distribution dans le désordre et la configuration de retransmission.

RTCDataChannel est désormais disponible avec la prise en charge de SCTP sur ordinateur et Android dans Google Chrome, Opera et Firefox.

Mise en garde : signalisation, STUN et TURN

WebRTC permet la communication peer-to-peer, mais il a toujours besoin de serveurs pour la signalisation afin d'échanger des métadonnées média et réseau pour amorcer une connexion peer.

WebRTC gère les NAT et les pare-feu grâce aux éléments suivants :

  • Le framework ICE pour établir le meilleur chemin réseau possible entre les pairs.
  • des serveurs STUN pour déterminer une adresse IP et un port accessibles au public pour chaque pair.
  • Serveurs TURN si la connexion directe échoue et que le relais de données est nécessaire.

Pour en savoir plus sur le fonctionnement de WebRTC avec les serveurs pour la signalisation et la mise en réseau, consultez WebRTC in the real world: STUN, TURN, and signaling.

Fonctionnalités

L'API RTCDataChannel est compatible avec un ensemble flexible de types de données. L'API est conçue pour imiter exactement WebSocket. RTCDataChannel est compatible avec les chaînes ainsi qu'avec certains types binaires en JavaScript, tels que Blob, ArrayBuffer et ArrayBufferView. Ces types peuvent être utiles lorsque vous travaillez avec le transfert de fichiers et les jeux multijoueurs.

RTCDataChannel peut fonctionner en mode non fiable et non ordonné (analogue au protocole de datagramme utilisateur ou UDP), en mode fiable et ordonné (analogue au protocole de contrôle de transmission ou TCP) et en modes partiellement fiables :

  • Le mode fiable et ordonné garantit la transmission des messages et l'ordre dans lequel ils sont distribués. Cela nécessite des ressources supplémentaires, ce qui peut ralentir ce mode.
  • Le mode non fiable et non ordonné ne garantit pas que chaque message parvienne à destination ni l'ordre dans lequel il y parvient. Cela élimine les frais généraux, ce qui permet à ce mode de fonctionner beaucoup plus rapidement.
  • Le mode fiable partiel garantit la transmission des messages dans des conditions spécifiques, comme un délai avant retransmission ou un nombre maximal de retransmissions. L'ordre des messages est également configurable.

Les performances des deux premiers modes sont à peu près identiques en l'absence de perte de paquets. Toutefois, en mode fiable et ordonné, un paquet perdu bloque les autres paquets derrière lui. De plus, le paquet perdu peut être obsolète au moment où il est retransmis et arrive. Il est bien sûr possible d'utiliser plusieurs canaux de données dans la même application, chacun avec sa propre sémantique fiable ou non fiable.

Voici un tableau utile tiré de High Performance Browser Networking par Ilya Grigorik :

TCPUDPSCTP
FiabilitéFiableManque de fiabilitéConfigurable
EnvoiDate de la commandeNon ordonnéeConfigurable
TransmissionOrienté octetAxé sur les messagesAxé sur les messages
Contrôle de fluxOuiNonOui
Contrôle de la congestionOuiNonOui

Vous allez ensuite apprendre à configurer RTCDataChannel pour utiliser le mode fiable et ordonné ou le mode non fiable et non ordonné.

Configurer les canaux de données

Plusieurs démos simples de RTCDataChannel sont disponibles en ligne :

Dans ces exemples, le navigateur établit une connexion pair à pair avec lui-même, puis crée un canal de données et envoie un message via la connexion pair à pair. Il crée ensuite un canal de données et envoie le message via la connexion pair à pair. Enfin, votre message s'affiche dans la zone de l'autre côté de la page.

Le code pour commencer est court :

const peerConnection = new RTCPeerConnection();

// Establish your peer connection using your signaling channel here
const dataChannel =
  peerConnection.createDataChannel("myLabel", dataChannelOptions);

dataChannel.onerror = (error) => {
  console.log("Data Channel Error:", error);
};

dataChannel.onmessage = (event) => {
  console.log("Got Data Channel Message:", event.data);
};

dataChannel.onopen = () => {
  dataChannel.send("Hello World!");
};

dataChannel.onclose = () => {
  console.log("The Data Channel is Closed");
};

L'objet dataChannel est créé à partir d'une connexion pair à pair déjà établie. Vous pouvez le créer avant ou après la signalisation. Vous transmettez ensuite un libellé pour distinguer ce canal des autres, ainsi qu'un ensemble de paramètres de configuration facultatifs :

const dataChannelOptions = {
  ordered: false, // do not guarantee order
  maxPacketLifeTime: 3000, // in milliseconds
};

Il est également possible d'ajouter une option maxRetransmits (nombre de tentatives avant échec), mais vous ne pouvez spécifier que maxRetransmits ou maxPacketLifeTime, et non les deux. Pour la sémantique UDP, définissez maxRetransmits sur 0 et ordered sur false. Pour en savoir plus, consultez les RFC de l'IETF suivantes : Stream Control Transmission Protocol et Stream Control Transmission Protocol Partial Reliability Extension.

  • ordered : indique si le canal de données doit garantir l'ordre ou non.
  • maxPacketLifeTime : délai maximal pour essayer de retransmettre un message ayant échoué
  • maxRetransmits : nombre maximal de tentatives de retransmission d'un message ayant échoué
  • protocol : permet d'utiliser un sous-protocole qui fournit des méta-informations à l'application.
  • negotiated : si la valeur est définie sur "true", la configuration automatique d'un canal de données sur l'autre pair est supprimée, ce qui vous permet de créer votre propre canal de données avec le même ID de l'autre côté.
  • id : vous permet de fournir votre propre ID pour la chaîne, qui ne peut être utilisé qu'en combinaison avec negotiated défini sur true.

La plupart des utilisateurs n'ont besoin d'utiliser que les trois premières options : ordered, maxPacketLifeTime et maxRetransmits. Avec SCTP (désormais utilisé par tous les navigateurs compatibles avec WebRTC), les valeurs "fiable" et "ordonné" sont définies sur "true" par défaut. Il est judicieux d'utiliser les options "Non fiable" et "Non ordonné" si vous souhaitez un contrôle total depuis la couche d'application, mais dans la plupart des cas, une fiabilité partielle est utile.

Notez que, comme avec WebSocket, RTCDataChannel déclenche des événements lorsqu'une connexion est établie, fermée ou en cas d'erreur, et lorsqu'il reçoit un message de l'autre pair.

Est-ce sûr ?

Le chiffrement est obligatoire pour tous les composants WebRTC. Avec RTCDataChannel, toutes les données sont sécurisées avec le protocole Datagram Transport Layer Security (DTLS). DTLS est un dérivé de SSL, ce qui signifie que vos données seront aussi sécurisées que si vous utilisiez une connexion SSL standard. DTLS est normalisé et intégré à tous les navigateurs compatibles avec WebRTC. Pour en savoir plus, consultez le wiki Wireshark.

Modifier votre façon de penser aux données

La gestion de grandes quantités de données peut être un point sensible en JavaScript. Comme l'ont souligné les développeurs de Sharefest, cela nécessitait de repenser les données. Si vous transférez un fichier plus volumineux que la quantité de mémoire dont vous disposez, vous devez trouver de nouvelles façons d'enregistrer ces informations. C'est là qu'interviennent des technologies telles que l'API FileSystem, comme vous le verrez ensuite.

Créer une application de partage de fichiers

Il est désormais possible de créer une application Web capable de partager des fichiers dans le navigateur avec RTCDataChannel. En s'appuyant sur RTCDataChannel, les données des fichiers transférés sont chiffrées et ne transitent pas par les serveurs d'un fournisseur d'applications. Cette fonctionnalité, combinée à la possibilité de se connecter à plusieurs clients pour un partage plus rapide, fait du partage de fichiers WebRTC un candidat idéal pour le Web.

Pour réussir le transfert, vous devez suivre plusieurs étapes :

  1. Lisez un fichier en JavaScript à l'aide de l'API File.
  2. Établissez une connexion peer-to-peer entre les clients avec RTCPeerConnection.
  3. Créez un canal de données entre les clients avec RTCDataChannel.

Plusieurs points sont à prendre en compte lorsque vous essayez d'envoyer des fichiers via RTCDataChannel :

  • Taille du fichier : si la taille du fichier est raisonnablement petite et peut être stockée et chargée en tant que Blob, vous pouvez la charger en mémoire à l'aide de l'API File, puis envoyer le fichier tel quel via un canal fiable (toutefois, n'oubliez pas que les navigateurs imposent des limites à la taille maximale des transferts). Plus la taille du fichier est importante, plus les choses se compliquent. Lorsqu'un mécanisme de segmentation est requis, les segments de fichier sont chargés et envoyés à un autre pair, accompagnés de métadonnées chunkID pour que le pair puisse les reconnaître. Notez que, dans ce cas, vous devez également enregistrer les blocs d'abord dans un espace de stockage hors connexion (par exemple, à l'aide de l'API FileSystem) et les enregistrer sur le disque de l'utilisateur uniquement lorsque vous disposez du fichier dans son intégralité.
  • Taille des blocs : il s'agit des plus petits "atomes" de données pour votre application. Le découpage en blocs est nécessaire, car il existe actuellement une limite de taille d'envoi (qui sera corrigée dans une future version des canaux de données). La taille maximale recommandée pour les blocs est actuellement de 64 Kio.

Une fois le fichier entièrement transféré, il peut être téléchargé à l'aide d'une balise d'ancrage :

function saveFile(blob) {
  const link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = 'File Name';
  link.click();
};

Les applications de partage de fichiers sur PubShare et GitHub utilisent cette technique. Elles sont toutes les deux Open Source et constituent une bonne base pour une application de partage de fichiers basée sur RTCDataChannel.

Que pouvez-vous faire ?

RTCDataChannel ouvre la voie à de nouvelles façons de créer des applications pour le partage de fichiers, les jeux multijoueurs et la diffusion de contenu.

  • Partage de fichiers peer-to-peer tel que décrit précédemment
  • Jeux multijoueurs associés à d'autres technologies, comme WebGL, comme dans BananaBread de Mozilla
  • La diffusion de contenu réinventée par PeerCDN, un framework qui diffuse des composants Web via la communication de données peer-to-peer

Modifier la façon dont vous créez des applications

Vous pouvez désormais proposer des applications plus attrayantes en utilisant des connexions hautes performances et à faible latence via RTCDataChannel. Les frameworks, tels que PeerJS et le SDK WebRTC PubNub, facilitent l'implémentation de RTCDataChannel, et l'API est désormais largement compatible avec les plates-formes.

L'avènement de RTCDataChannel peut changer votre façon de penser au transfert de données dans le navigateur.

En savoir plus