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

L'envoi de données entre deux navigateurs à des fins de communication, de jeu ou de transfert de fichiers peut s'avérer assez complexe. Cela implique de configurer et de payer un serveur pour relayer les données, et éventuellement 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.

Vous pouvez résoudre ces problèmes en utilisant l'API RTCDataChannel de WebRTC pour transférer les données directement d'un pair à un autre. Cet article présente les principes de base de la configuration et de l'utilisation des canaux de données, ainsi que les cas d'utilisation courants sur le Web aujourd'hui.

Pourquoi un autre canal de données ?

Nous avons WebSocket, AJAX et les Server Sent Events (Événements envoyés par le serveur). Pourquoi avons-nous besoin d'un autre canal de communication ? WebSocket est bidirectionnel, mais toutes ces technologies sont conçues pour la communication avec ou depuis un serveur.

RTCDataChannel adopte une approche différente:

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

RTCDataChannel est désormais compatible 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 multimédias et réseau pour amorcer une connexion pair.

WebRTC gère les NAT et les pare-feu avec:

  • Le framework ICE pour établir le meilleur chemin réseau possible entre les pairs.
  • Serveurs STUN pour vérifier une adresse IP et un port accessibles publiquement pour chaque pair.
  • Serveurs TURN si la connexion directe échoue et si un relais de données est requis.

Pour en savoir plus sur le fonctionnement de WebRTC avec les serveurs pour la mise en réseau et la signalisation, consultez WebRTC dans le monde réel: STUN, TURN et signaling.

Les capacités

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

RTCDataChannel peut fonctionner dans des modes non fiables et non ordonnés (analogiques au protocole de datagramme utilisateur ou UDP), dans des modes fiables et ordonnés (analogiques au protocole de contrôle de transmission ou TCP), et dans des modes partiellement fiables:

  • Le mode fiable et ordonné garantit la transmission des messages, ainsi que l'ordre dans lequel ils sont distribués. Cela entraîne une surcharge supplémentaire et peut donc ralentir ce mode.
  • Le mode "non fiable et non ordonné" ne garantit pas que chaque message arrivera de l'autre côté de la ligne ni dans quel ordre. Cela élimine la surcharge, ce qui permet à ce mode de fonctionner beaucoup plus rapidement.
  • Le mode fiable partiel garantit la transmission du message dans une condition spécifique, comme un délai avant expiration de retransmission ou un nombre maximal de retransmissions. Vous pouvez également configurer l'ordre des messages.

Les performances des deux premiers modes sont à peu près identiques en l'absence de perte de paquets. Cependant, en mode fiable et ordonné, un paquet perdu entraîne le blocage d'autres paquets en arrière-plan, et le paquet perdu peut être obsolète au moment de sa retransmission et de son arrivée. 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 peu fiable.

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

TCPUDPSCTP
FiabilitéFiableDéfectueuseConfigurable
DiffusionDate de la commandeNon triéConfigurable
TransmissionOrientée octetOrientées messagesOrientées messages
Contrôle de fluxOuiNonOui
Contrôle des embouteillagesOuiNonOui

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

Configurer des canaux de données

Il existe plusieurs démonstrations simples de RTCDataChannel en ligne:

Dans ces exemples, le navigateur établit une connexion de pair avec lui-même, puis crée un canal de données et envoie un message via cette connexion. Elle crée ensuite un canal de données et envoie le message via la connexion au pair. Enfin, votre message apparaît dans la zone située 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 de pairs déjà établie. Il peut être créé avant ou après le signalement. 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 (le nombre de tentatives à effectuer avant un échec), mais vous ne pouvez spécifier que maxRetransmits ou maxPacketLifeTime, mais pas les deux. Pour la sémantique UDP, définissez maxRetransmits sur 0 et ordered sur false. Pour en savoir plus, consultez les RFC IETF: 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 une tentative de retransmission d'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 sur l'application.
  • negotiated: si la valeur est "true", supprime la configuration automatique d'un canal de données sur l'autre pair, vous permettant ainsi de créer vous-même un 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'avec le paramètre negotiated défini sur true.

Les seules options dont la plupart des utilisateurs ont besoin sont les trois premières: ordered, maxPacketLifeTime et maxRetransmits. Avec le protocole SCTP (désormais utilisé par tous les navigateurs compatibles avec WebRTC), la fiabilité et l'ordre sont la valeur "true" par défaut. Il est judicieux d'utiliser des données peu fiables et non ordonnées si vous souhaitez contrôler entièrement la couche de l'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 génère des erreurs, 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 via DTLS (Datagram Transport Layer Security). DTLS est un dérivé de SSL, ce qui signifie que vos données sont aussi sécurisées que n'importe quelle connexion SSL standard. DTLS est standardisé et intégré à tous les navigateurs compatibles avec WebRTC. Pour en savoir plus, consultez le wiki de Wireshark.

Changer la façon dont vous envisagez les données

Le traitement de grandes quantités de données peut s'avérer problématique en JavaScript. Comme l'ont souligné les développeurs de Sharefest, cette nouvelle approche nécessitait une nouvelle approche des données. Si vous transférez un fichier qui dépasse la quantité de mémoire dont vous disposez, vous devez trouver de nouvelles façons d'enregistrer ces informations. C'est là que les technologies, telles que l'API FileSystem, entrent en jeu, comme vous allez le voir ci-après.

Créer une application de partage de fichiers

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

Vous devez suivre plusieurs étapes pour que le transfert aboutisse:

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

Vous devez tenir compte de plusieurs points lorsque vous tentez d'envoyer des fichiers via RTCDataChannel:

  • Taille du fichier:si la taille du fichier est raisonnablement petite et qu'il peut être stocké et chargé en tant qu'objet blob, vous pouvez le charger en mémoire à l'aide de l'API File, puis envoyer le fichier en l'état via un canal fiable (notez toutefois que les navigateurs imposent des limites de taille de transfert maximale). Plus la taille des fichiers augmente, plus les choses se compliquent. Lorsqu'un mécanisme de fragmentation est nécessaire, les fragments de fichiers sont chargés et envoyés à un autre pair, accompagnés de métadonnées chunkID afin que celui-ci puisse les reconnaître. Notez que, dans ce cas, vous devez également d'abord enregistrer les fragments 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 fragments:il s'agit des plus petits "atomes" de données de votre application. La segmentation est nécessaire, car la taille d'envoi est actuellement limitée (ce problème sera résolu dans une prochaine version des canaux de données). La taille maximale de fragment recommandée est actuellement de 64 Kio.

Une fois le fichier transféré vers l'autre côté, vous pouvez le télécharger à 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();
};

Ces applications de partage de fichiers sur PubShare et GitHub utilisent cette technique. Ces deux formats sont Open Source et offrent 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 comme décrit précédemment
  • Le jeu multijoueur, associé à d'autres technologies, comme WebGL, comme le montre le jeu BananaBread de Mozilla
  • La diffusion de contenu a été réinventée par PeerCDN, un framework qui fournit des éléments Web via une communication de données peer-to-peer.

Changez 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 de PubNub facilitent l'implémentation de RTCDataChannel, et l'API est désormais compatible avec un large éventail de plates-formes.

L'avènement de RTCDataChannel peut changer la façon dont vous envisagez le transfert de données dans le navigateur.

En savoir plus