Canales de datos QUIC: primeros pasos


Los DataChannels basados ​​en QUIC se consideran una alternativa al transporte SCTP actual. El grupo de trabajo de Google WebRTC ya está experimentando con ellos:


Probémoslo también. Para hacer esto, crearemos una aplicación de una página similar al ejemplo de un canal WebRTC para enviar texto ; este es un ejemplo totalmente funcional (sin servidores de señalización), que, además, facilitará la comparación de enfoques para implementar WebCTC DataChannels.

Antes de comenzar, recordemos los conceptos básicos del DataChannel .

Brevemente sobre DataChannel


Los DataChannels de WebRTC permiten a los participantes intercambiar datos arbitrarios. Pueden ser confiables, lo que es muy útil al transferir archivos, y poco confiables, lo cual es aceptable para obtener información sobre las posiciones en los juegos. La API es una extensión de RTCPeerConnection y tiene este aspecto:

 const dc = pc.createDataChannel("some label string"); // ,    – ,   – //    send dc.send("some string"); //    otherPc.addEventListener('datachannel', e => { const channel = e.channel; channel.onmessage = event => { console.log('received', event.data); }); }); 

En la página oficial de muestras de WebRTC hay ejemplos de envío de cadenas y datos binarios .

DataChannel usa SCTP . Funciona en paralelo con el transporte RTP para transmisiones de audio y video. A diferencia de UDP, que es comúnmente utilizado por transmisiones de audio y video, SCTP ofrece muchas otras características, como canales de multiplexación a través de una sola conexión o modos confiables, parcialmente confiables (es decir, confiables, pero desordenados) y poco confiables.

Google introdujo QUIC en 2012 (puede encontrar más información sobre el historial del protocolo y sus matices en nuestro otro material : la nota del traductor). Al igual que WebRTC, el protocolo QUIC también se ha tomado bajo el ala del IETF y ahora es HTTP / 3 . QUIC tiene una serie de grandes innovaciones, tales como: latencia reducida, cálculo de ancho de banda basado en control de congestión, corrección de retardo directo (FEC) e implementación en el espacio del usuario (en lugar del kernel) para un desplazamiento más rápido.

QUIC podría ser una alternativa a RTCP para WebRTC, como un transporte para DataChannel. El experimento actual está tratando de evitar el uso de la API RTCPeerConnection (¡ y SDP! ) Mediante el uso de una versión separada del transporte ICE. Piense en ello como una conexión virtual que agrega un poco de seguridad y una gran cantidad de NAT transversal .

En el siguiente video, Ian Swett, del equipo de redes de Chrome, explica este concepto. Y aunque este discurso ha tenido varios años, aún proporciona información adicional sobre el tema:


Primeros pasos con QUIC


Afortunadamente, la mayor parte del código del artículo de 2015 sigue siendo relevante y se adapta fácilmente a la nueva API. Vamos a resolverlo.

Clone el código desde aquí o pruébelo aquí . Tenga en cuenta que Chrome (la versión 73+ ahora es Canarias) debe ejecutarse con indicadores especiales para que el experimento funcione localmente:

 google-chrome-unstable --enable-blink-features=RTCQuicTransport,RTCIceTransportExtension 

Configuración de transporte ICE


La especificación RTCIceTransport se basa en ORTC, por lo que la configuración es similar al código anterior:

 const ice1 = new RTCIceTransport(); ice1.onstatechange = function() { console.log('ICE transport 1 state change', ice1.state); }; const ice2 = new RTCIceTransport(); ice2.onstatechange = function() { console.log('ICE transport 2 state change', ice2.state); }; //  ICE- ice1.onicecandidate = function(evt) { console.log('1 -> 2', evt.candidate); if (evt.candidate) { ice2.addRemoteCandidate(evt.candidate); } }; ice2.onicecandidate = function(evt) { console.log('2 -> 1', evt.candidate); if (evt.candidate) { ice1.addRemoteCandidate(evt.candidate); } }; //  ICE- ice1.start(ice2.getLocalParameters(), 'controlling'); ice2.start(ice1.getLocalParameters(), 'controlled'); ice1.gather(iceOptions); ice2.gather(iceOptions); 

Tenga en cuenta que esta API no tiene RTCIceGatherer, a diferencia de ORTC. Porque ya tenemos todo lo que necesitamos para instalar el transporte ICE.

Configurar el transporte QUIC


 const quic1 = new RTCQuicTransport(ice1); quic1.onstatechange = function() { console.log('QUIC transport 1 state change', quic1.state); }; const quic2 = new RTCQuicTransport(ice2); quic2.onstatechange = function() { console.log('QUIC transport 2 state change', quic2.state); }; //     QUIC quic2.addEventListener('quicstream', (e) => { console.log('QUIC transport 2 got a stream', e.stream); receiveStream = e.stream; }); 

Aquí, el experimento parte de una especificación que usa autenticación basada en certificados. En cambio, se usa la clave pública, como dice la publicación de Google Developers :
La conexión RTCQuicTransport está configurada con una clave pública API. Actualmente no planeamos que esta API reemplace la validación original. Será reemplazado por un certificado remoto de señalización para validar certificados autofirmados, cuando QUIC comience a admitirlo en Chromium.
Hasta ahora todo bien.

QUICStream para enviar y recibir datos


Usar QUICStream es un poco más complicado que WebRTC DataChannel. La API Streams ( ver detalles sobre MDN ) creada por el grupo de trabajo WHATWG ha sido aceptada pero no implementada .

Creamos sendStream solo después de que el transporte QUIC entra en el estado "conectado"; en un estado diferente, esto conduciría a un error:

 quic1.onstatechange = function() { console.log('QUIC transport 1 state change', quic1.state); if (quic1.state === 'connected' && !sendStream) { sendStream = quic1.createStream('webrtchacks'); //   createDataChannel. document.getElementById('sendButton').disabled = false; document.getElementById('dataChannelSend').disabled = false; } }; 

Luego adjuntamos los controladores al botón de enviar y al campo de entrada: después de hacer clic en el botón, el texto del campo de entrada se codifica en Uint8Array y se escribe en la secuencia:

 document.getElementById('sendButton').onclick = () => { const rawData = document.getElementById('dataChannelSend').value; document.getElementById('dataChannelSend').value = ''; //  Uint8Array. ,       TextEncoder. const data = encoder.encode(rawData); sendStream.write({ data, }); }; 

La primera entrada activará el evento onquicstream en el transporte QUIC remoto:

 //     QUIC quic2.addEventListener('quicstream', (e) => { console.log('QUIC transport 2 got a stream', e.stream); receiveStream = e.stream; receiveStream.waitForReadable(1) .then(ondata); }); 

... y luego esperamos que los datos sean legibles:
 function ondata() { const buffer = new Uint8Array(receiveStream.readBufferedAmount); const res = receiveStream.readInto(buffer); const data = decoder.decode(buffer); document.getElementById('dataChannelReceive').value = data; receiveStream.waitForReadable(1) .then(ondata); } 

Todos los datos de receiveStream se leerán, decodificarán en texto y se colocarán en el campo de salida. Y así, cada vez que aparecen datos legibles.

Conclusión y comentarios


Espero que este ejemplo sea más fácil de entender que el similar en el blog de Google . Este método no es adecuado para conexiones P2P, el DataChannel en SCTP ya está funcionando bien para ellos. Sin embargo, esta puede ser una alternativa interesante a los sockets web con un servidor QUIC en el otro extremo. Hasta que esto suceda, debe determinar una forma decente de trabajar con canales poco confiables y desordenados. En mi opinión, las sugerencias de la publicación antes mencionada se parecen más a hacks que a decisiones.

Tampoco está claro qué comentarios esperan los desarrolladores del exterior. "Ya implementamos la especificación en lugar de esculpir nuevamente los atajos, que permanecerán con nosotros durante varios años", parece demasiado obvio. Además, la opinión general de la comunidad tiende a usar flujos de WHATWG, lo que pone en evidencia a los desarrolladores que piden probar su propia API para leer datos.

También me gustaría que SCTP en Chromium tenga características adicionales. Por ejemplo, esta consulta sobre DataChannel , la mejor calificada, por cierto, ha permanecido casi intacta durante tres años. No está del todo claro por qué hay un enfoque en QUIC cuando todavía hay tareas SCTP; sin embargo, esto no debería impedir que nadie pruebe QUIC y reciba comentarios sobre los resultados.

Comentario de voximplant


Una palabra para nuestro líder frontend irbisadm :
Durante mucho tiempo, nuestros SDK se han utilizado para señalizar un socket web. Este es un estándar excelente y probado en el tiempo, pero tiene algunos problemas. El primero es TCP. Y TCP no es tan bueno y rápido en redes móviles, además no admite roaming entre redes. En segundo lugar, a menudo es textual (también hay un modo binario, pero rara vez lo ves).

Recientemente lanzamos una prueba beta cerrada del protocolo de señalización en el DataChannel. Este protocolo tampoco está exento de inconvenientes, pero dado que funciona en redes deficientes y en itinerancia, conquista a primera vista. ¿Has cambiado la red? No es necesario recrear la conexión. ICE Restart en la mayoría de los casos ayudará a encontrar una nueva forma de tráfico. Pero, como dije, el protocolo todavía tiene inconvenientes: no todos los navegadores admiten todas las extensiones de protocolo, como la entrega garantizada y el soporte de pedidos de paquetes; Además, el protocolo no admite gzip para el modo de texto fuera de la caja. Pero todos estos problemas pueden resolverse en el lado de la aplicación.

Source: https://habr.com/ru/post/441168/


All Articles