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

Enviar datos entre dos navegadores para la comunicación, los videojuegos o la transferencia de archivos puede ser un proceso bastante complicado. Requiere configurar y pagar por un servidor que retransmita datos, y quizás escalarlo a varios centros de datos. En esta situación, existe el potencial de una latencia alta y es 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 describen los aspectos básicos de cómo configurar y usar canales de datos, así como los casos de uso comunes en la Web en la actualidad.

¿Por qué usar 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 hacia o desde 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 transmisión (SCTP), lo que permite la configuración configurable de entrega y retransmisión de semántica de entrega desordenada.

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 de par.

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 intercambio de tráfico.
  • Servidores TURN si la conexión directa falla y se requiere la retransmisión de datos.

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

Las capacidades

La API de RTCDataChannel admite un conjunto flexible de tipos de datos. La API está diseñada para imitar exactamente WebSocket, y RTCDataChannel admite cadenas, al igual que algunos tipos de objetos binarios en JavaScript, como Blob, ArrayBuffer y ArrayBufferView. Estos tipos pueden ser útiles cuando trabajas con transferencias de archivos y 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 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 no ordenado 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 poco confiable.

Esta es una tabla útil de Herramientas de redes de navegador de alto rendimiento de Ilya Grigorik:

TCPUDPSCTP
ConfiabilidadConfiableNo confiablesConfigurable
EntregaOrdenadoSin ordenConfigurable
TransmisiónOrientada a bytesOrientado a los mensajesOrientado a los mensajes
Control de flujoNo
Control de la 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. A continuación, crea un canal de datos y envía el mensaje a través de la conexión de intercambio de tráfico. Por último, tu mensaje aparece en el cuadro que está al 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 o no el orden.
  • maxPacketLifeTime: El tiempo máximo para intentar retransmitir un mensaje con errores
  • 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 que admiten 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 vas a transferir un archivo que supera la cantidad de memoria 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.

Compila 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 bastante 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 tal como está a través de un canal confiable (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 de los archivos, todo se complica. Cuando se requiere un mecanismo de fragmentación, los bloques de archivos se cargan y se envían a otro par, junto con los metadatos chunkID para que el par pueda reconocerlos. Ten en cuenta que, en este caso, primero debes guardar los fragmentos en el almacenamiento sin conexión (por ejemplo, con la API de FileSystem) y guardarlos en el disco del usuario solo cuando tengas el archivo completo.
  • Tamaño del segmento: Son los “átomos” de datos más pequeños para tu app. La fragmentación es obligatoria porque actualmente hay un límite de tamaño de envío (aunque 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 tu forma de compilar apps

Ahora puedes proporcionar apps más atractivas con 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 es compatible con todas las plataformas.

La llegada de RTCDataChannel puede cambiar la forma en que piensas sobre la transferencia de datos en el navegador.

Más información