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

El envío de datos entre dos navegadores para la comunicación, el juego o la transferencia de archivos puede ser un proceso bastante complejo. Requiere configurar y pagar por un servidor para retransmitir datos, y tal vez escalar esto a varios centros de datos. En esta situación, existe potencial de latencia alta y es difícil mantener la privacidad de los datos.

Estos problemas se pueden solucionar usando la API de RTCDataChannel de WebRTC para transferir datos directamente de un intercambio de tráfico a otro. En este artículo, se abordan los aspectos básicos de la configuración y el uso de los canales de datos, además de los casos de uso comunes en la Web en la actualidad.

¿Por qué otro canal de datos?

Tenemos WebSocket, AJAX y eventos enviados del 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 permite la conectividad entre pares. Esto puede generar una latencia más baja, sin servidores intermedios y menos “saltos”.
  • RTCDataChannel usa el Protocolo de transmisión de control de transmisión (SCTP), lo que permite una semántica de entrega configurable de entrega desordenada y una configuración de retransmisión.

RTCDataChannel ahora está disponible en Google Chrome, Opera y Firefox para computadoras y dispositivos Android compatibles con SCTP.

Una advertencia: Señales, STUN y TURN

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

WebRTC lidia con las NAT y los firewalls de las siguientes maneras:

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

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

Las funciones

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

RTCDataChannel puede funcionar en modo no confiable y desordenado (análogo al protocolo de datagramas de usuario o UDP), 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, así como el orden en que se entregan. Esto requiere una sobrecarga adicional, por lo que es posible que el modo sea más lento.
  • El modo poco confiable y desordenado no garantiza que todos los mensajes lleguen al otro lado ni en qué orden lo hacen. Esto quita la sobrecarga, lo que permite que este modo funcione mucho más rápido.
  • El modo parcial confiable garantiza la transmisión de mensajes en una condición específica, como un tiempo de espera de retransmisión o una cantidad máxima de retransmisiones. También se puede configurar el orden de los mensajes.

El rendimiento de los dos primeros modos es casi 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 en el momento en que se vuelve a transmitir 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.

En este documento, encontrarás una tabla útil de High Performance Browser Networking de Ilya Grigorik:

TCPUDPSCTP
ConfiabilidadConfiableNo confiableConfigurable
EntregaOrdenadoSin ordenarConfigurable
TransmisiónOrientado a bytesOrientada a los mensajesOrientada 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 sencillas de RTCDataChannel en línea:

En estos ejemplos, el navegador se conecta a sí mismo con intercambio de tráfico, 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 lo largo de la conexión de par. 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 par ya establecida. Se puede crear antes o después de que se produzca la señalización. Luego, debes pasar una etiqueta para distinguir este canal de otros, además de 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 probar antes de que falle), pero solo puedes especificar maxRetransmits o maxPacketLifeTime, no ambas. Para la semántica de UDP, establece maxRetransmits en 0 y ordered en false. Para obtener más información, consulta las siguientes 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 transmisión.

  • ordered: Si el canal de datos debe garantizar el orden o no
  • maxPacketLifeTime: El tiempo máximo para intentar retransmitir un mensaje con errores
  • maxRetransmits: La cantidad máxima de veces que se debe intentar volver a transmitir 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 intercambio de tráfico y proporciona tu propia manera de crear un canal de datos con el mismo ID en el otro lado.
  • 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 necesita usar son las tres primeras: ordered, maxPacketLifeTime y maxRetransmits. Con SCTP (que ahora se usa en todos los navegadores compatibles con WebRTC), el estado fiable y ordenado es verdadero de forma predeterminada. Tiene sentido usar valores poco confiables y desordenados si quieres tener un 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 una conexión, se cierra o cuando se produce un error, y cuando recibe un mensaje del otro intercambio de tráfico.

¿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). DTLS es un derivado de SSL, lo que significa que tus datos estarán tan seguros como cualquier conexión estándar basada en SSL. DTLS está estandarizado y se integra en todos los navegadores que admiten WebRTC. Para obtener más información, consulta la wiki de Wireshark.

Cambia tu percepción de los datos

Administrar grandes cantidades de datos puede ser un punto débil en JavaScript. Como señalaron los desarrolladores de Sharefest, esto requería pensar en los datos de otra manera. Si vas a transferir un archivo que supera la cantidad de memoria que tienes disponible, debes pensar en nuevas formas de guardar esta información. Aquí es donde entran en juego las tecnologías, como la API de FileSystem, como verás a continuación.

Compila una app para compartir archivos

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

Se requieren varios pasos para realizar una transferencia exitosa:

  1. Lee un archivo en JavaScript con la API de File.
  2. Establece una conexión de intercambio de tráfico entre clientes con RTCPeerConnection.
  3. Crea un canal de datos entre clientes con RTCDataChannel.

Existen varios puntos que debes considerar cuando intentes 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 BLOB, puedes cargarlo en la memoria con la API de File y, luego, enviar el archivo a través de un canal confiable tal como está (aunque los navegadores imponen límites al tamaño máximo de transferencia). A medida que aumenta el tamaño de los archivos, las cosas se complican. Cuando se requiere un mecanismo de fragmentación, los fragmentos de archivos se cargan y se envían a otro par, acompañados de metadatos de chunkID, para que la app pueda reconocerlos. Ten en cuenta que, en este caso, también debes guardar los fragmentos primero en un 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 de fragmentación: Estos son los "átomos" de datos más pequeños de tu app. La fragmentación es obligatoria 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 64 KiB.

Una vez que el archivo se haya transferido por completo al otro lado, se podrá descargar con una etiqueta de anclaje:

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 el uso compartido de archivos, los juegos multijugador y la 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.
  • PeerCDN, un marco de trabajo que entrega activos web a través de la comunicación de datos entre pares, es la reinvención de la publicación de contenido.

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 es compatible con varias plataformas.

La llegada de RTCDataChannel puede cambiar tu forma de pensar sobre la transferencia de datos en el navegador.

Más información