Cómo enviar datos entre navegadores con canales de datos de WebRTC

Enviar datos entre dos navegadores para la comunicación, los juegos o la transferencia de archivos puede ser un proceso bastante complejo. Requiere configurar y pagar un servidor para retransmitir datos y, tal vez, escalarlo a varios centros de datos. En este caso, es posible que haya una latencia alta y sea difícil mantener la privacidad de los datos.

Estos problemas se pueden aliviar con el uso de la API de RTCDataChannel de WebRTC para transferir datos directamente de un par a otro. En este artículo, se explican los conceptos básicos para configurar y usar canales de datos, así como los casos de uso comunes en la Web en la actualidad.

¿Por qué otro canal de datos?

Tenemos WebSocket, AJAX y Eventos enviados por el servidor. ¿Por qué necesitamos otro canal de comunicación? WebSocket es bidireccional, pero todas estas tecnologías están diseñadas para la comunicación desde o hacia un servidor.

RTCDataChannel adopta un enfoque diferente:

  • Funciona con la API de RTCPeerConnection, que habilita la conectividad entre pares. Esto puede generar una latencia más baja, sin un servidor intermediario y menos “saltos”.
  • RTCDataChannel usa el protocolo de transmisión de control de flujo (SCTP), que permite la configuración de retransmisión y entrega fuera de orden de semánticas de entrega configurables.

RTCDataChannel ahora está disponible con compatibilidad con SCTP en computadoras de escritorio y Android en Google Chrome, Opera y Firefox.

Una advertencia: señalización, STUN y TURN

WebRTC habilita la comunicación entre pares, pero aún necesita servidores para la señalización para intercambiar metadatos de red y multimedia para iniciar una conexión entre pares.

WebRTC se adapta a las NAT y los firewalls con lo siguiente:

  • El framework ICE para establecer la mejor ruta de red posible entre los pares.
  • Servidores STUN para determinar una IP y un puerto de acceso público para cada par
  • Servidores TURN si la conexión directa falla y se requiere la retransmisión de datos.

Para obtener más información sobre cómo funciona WebRTC con servidores para la señalización y las redes, consulta WebRTC en el mundo real: STUN, TURN y señalización.

Las capacidades

La API de RTCDataChannel admite un conjunto flexible de tipos de datos. La API está diseñada para imitar WebSocket exactamente, y RTCDataChannel admite cadenas, así como algunos de los tipos binarios en JavaScript, como Blob, ArrayBuffer y ArrayBufferView. Estos tipos pueden ser útiles cuando se trabaja con la transferencia de archivos y los juegos multijugador.

RTCDataChannel puede funcionar en modo no confiable y desordenado (análogo al protocolo de datagramas de usuario o UDP), modo confiable y ordenado (análogo al protocolo de control de transmisión o TCP) y modos confiables parciales:

  • El modo confiable y ordenado garantiza la transmisión de los mensajes y también el orden en que se entregan. Esto genera una sobrecarga adicional, lo que podría ralentizar este modo.
  • El modo no confiable y sin orden no garantiza que todos los mensajes lleguen al otro extremo ni en qué orden. Esto quita la sobrecarga, lo que permite que este modo funcione mucho más rápido.
  • El modo confiable parcial garantiza la transmisión de mensajes en condiciones específicas, como un tiempo de espera de retransmisión o una cantidad máxima de retransmisiones. El orden de los mensajes también se puede configurar.

El rendimiento de los dos primeros modos es aproximadamente el mismo cuando no hay pérdidas de paquetes. Sin embargo, en el modo confiable y ordenado, un paquete perdido hace que otros paquetes se bloqueen detrás de él, y el paquete perdido puede estar inactivo cuando se retransmite y llega. Por supuesto, es posible usar varios canales de datos dentro de la misma app, cada uno con su propia semántica confiable o no confiable.

Esta es una tabla útil de High Performance Browser Networking, de Ilya Grigorik:

TCPUDPSCTP
ConfiabilidadConfiableNo confiablesConfigurable
EntregaOrdenadoSin ordenConfigurable
TransmisiónOrientado a bytesOrientado a los mensajesOrientado a los mensajes
Control de flujoNo
Control de congestiónNo

A continuación, aprenderás a configurar RTCDataChannel para usar el modo confiable y ordenado o no confiable y no ordenado.

Configura canales de datos

Hay varias demostraciones simples de RTCDataChannel en línea:

En estos ejemplos, el navegador establece una conexión de par consigo mismo y, luego, crea un canal de datos y envía un mensaje a través de la conexión de par. Luego, crea un canal de datos y envía el mensaje a través de la conexión de pares. Por último, tu mensaje aparecerá en el cuadro del otro lado de la página.

El código para comenzar con esto es breve:

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");
};

El objeto dataChannel se crea a partir de una conexión de pares ya establecida. Se puede crear antes o después de que se produzca el indicador. Luego, pasas una etiqueta para distinguir este canal de los demás y un conjunto de parámetros de configuración opcionales:

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

También es posible agregar una opción maxRetransmits (la cantidad de veces que se debe intentar antes de que falle), pero solo puedes especificar maxRetransmits o maxPacketLifeTime, no ambos. Para la semántica de UDP, establece maxRetransmits en 0 y ordered en false. Para obtener más información, consulta estas RFC de IETF: Protocolo de transmisión de control de flujo y Extensión de confiabilidad parcial del protocolo de transmisión de control de flujo.

  • ordered: Indica si el canal de datos debe garantizar el orden o no.
  • maxPacketLifeTime: Es el tiempo máximo para intentar retransmitir un mensaje que falló.
  • maxRetransmits: Es la cantidad máxima de veces que se intentará retransmitir un mensaje con errores.
  • protocol: Permite usar un subprotocolo, que proporciona metainformación a la app.
  • negotiated: Si se establece como verdadero, quita la configuración automática de un canal de datos en el otro par y te proporciona tu propia forma de crear un canal de datos con el mismo ID en el otro extremo.
  • id: Te permite proporcionar tu propio ID para el canal, que solo se puede usar en combinación con negotiated establecido en true.

Las únicas opciones que la mayoría de las personas deben usar son las tres primeras: ordered, maxPacketLifeTime y maxRetransmits. Con SCTP (que ahora usan todos los navegadores compatibles con WebRTC), la confiabilidad y el orden son verdaderos de forma predeterminada. Tiene sentido usar no confiable y no ordenado si deseas tener control total desde la capa de la app, pero en la mayoría de los casos, la confiabilidad parcial es útil.

Ten en cuenta que, al igual que con WebSocket, RTCDataChannel activa eventos cuando se establece, cierra o genera errores una conexión, y cuando recibe un mensaje del otro par.

¿Es seguro?

La encriptación es obligatoria para todos los componentes de WebRTC. Con RTCDataChannel, todos los datos están protegidos con la seguridad de la capa de transporte de datagramas (DTLS). El DTLS es un derivado de SSL, lo que significa que tus datos estarán tan seguros como con cualquier conexión estándar basada en SSL. El DTLS está estandarizado y está integrado en todos los navegadores que admiten WebRTC. Para obtener más información, consulta la wiki de Wireshark.

Cambia tu forma de pensar sobre los datos

El manejo de grandes cantidades de datos puede ser un problema en JavaScript. Como señalaron los desarrolladores de Sharefest, esto requirió pensar en los datos de una manera nueva. Si transfieres un archivo que es más grande que la cantidad de memoria que tienes disponible, debes pensar en nuevas formas de guardar esta información. Aquí es donde entran en juego tecnologías como la API de FileSystem, como verás a continuación.

Crea una app para compartir archivos

Con RTCDataChannel, ahora es posible crear una app web que pueda compartir archivos en el navegador. Compilar en RTCDataChannel significa que los datos de archivos transferidos están encriptados y no tocan los servidores de un proveedor de apps. Esta funcionalidad, combinada con la posibilidad de conectarse a varios clientes para compartir archivos más rápido, hace que el uso compartido de archivos de WebRTC sea una opción sólida para la Web.

Se deben seguir varios pasos para que la transferencia se realice correctamente:

  1. Lee un archivo en JavaScript con la API de File.
  2. Realiza una conexión entre pares entre clientes con RTCPeerConnection.
  3. Crea un canal de datos entre clientes con RTCDataChannel.

Hay varios puntos que debes tener en cuenta cuando intentas enviar archivos a través de RTCDataChannel:

  • Tamaño del archivo: Si el tamaño del archivo es razonablemente pequeño y se puede almacenar y cargar como un solo BLOB, puedes cargarlo en la memoria con la API de File y, luego, enviarlo a través de un canal confiable tal como está (aunque ten en cuenta que los navegadores imponen límites en el tamaño máximo de transferencia). A medida que aumenta el tamaño del archivo, las cosas se complican. Cuando se requiere un mecanismo de fragmentación, los fragmentos de archivo se cargan y se envían a otro par, acompañados de metadatos chunkID para que el par pueda reconocerlos. Ten en cuenta que, en este caso, también debes guardar los fragmentos primero en el almacenamiento sin conexión (por ejemplo, con la API de FileSystem) y en el disco del usuario solo cuando tengas el archivo completo.
  • Tamaño de fragmento: Son los "átomos" más pequeños de datos de tu app. El fragmento es necesario porque, actualmente, existe un límite de tamaño de envío (aunque esto se corregirá en una versión futura de los canales de datos). La recomendación actual para el tamaño máximo de fragmento es de 64 KiB.

Una vez que el archivo se transfiera por completo al otro lado, se puede descargar con una etiqueta de ancla:

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

Estas apps de uso compartido de archivos en PubShare y GitHub usan esta técnica. Ambos son de código abierto y proporcionan una buena base para una app de uso compartido de archivos basada en RTCDataChannel.

Entonces, ¿qué puedes hacer?

RTCDataChannel abre las puertas a nuevas formas de compilar apps para compartir archivos, juegos multijugador y entrega de contenido.

  • Uso compartido de archivos entre pares, como se describió anteriormente
  • Juegos multijugador combinados con otras tecnologías, como WebGL, como se ve en BananaBread de Mozilla
  • La entrega de contenido se reinventó con PeerCDN, un framework que entrega recursos web a través de la comunicación de datos entre pares.

Cambia la forma en que compilas apps

Ahora puedes proporcionar apps más atractivas mediante conexiones de alto rendimiento y baja latencia a través de RTCDataChannel. Los frameworks, como PeerJS y el SDK de WebRTC de PubNub, facilitan la implementación de RTCDataChannel, y la API ahora tiene una amplia compatibilidad con todas las plataformas.

El advenimiento de RTCDataChannel puede cambiar la forma en que piensas sobre la transferencia de datos en el navegador.

Más información