Enviar dados entre navegadores com canais de dados WebRTC

Enviar dados entre dois navegadores para comunicação, jogos ou transferência de arquivos pode ser um processo bastante complicado. É necessário configurar e pagar por um servidor para retransmitir dados e talvez dimensionar isso para vários data centers. Nesse cenário, há potencial para alta latência e é difícil manter os dados particulares.

Esses problemas podem ser atenuados usando a API RTCDataChannel do WebRTC para transferir dados diretamente de um peer para outro. Este artigo aborda os conceitos básicos de como configurar e usar canais de dados, além dos casos de uso comuns na Web hoje.

Por que outro canal de dados?

Temos WebSocket, AJAX e Eventos enviados pelo servidor. Por que precisamos de outro canal de comunicação? O WebSocket é bidirecional, mas todas essas tecnologias são projetadas para comunicação com ou de um servidor.

O RTCDataChannel usa uma abordagem diferente:

  • Ele funciona com a API RTCPeerConnection, que permite a conectividade ponto a ponto. Isso pode resultar em menor latência, sem servidor intermediário e com menos "saltos".
  • O RTCDataChannel usa o Stream Control Transmission Protocol (SCTP), permitindo semântica de entrega configurável, entrega fora de ordem e configuração de retransmissão.

O RTCDataChannel já está disponível com suporte a SCTP em computadores e dispositivos Android no Google Chrome, Opera e Firefox.

Uma observação: sinalização, STUN e TURN

O WebRTC permite a comunicação ponto a ponto, mas ainda precisa de servidores para sinalização, troca de mídia e metadados de rede para inicializar uma conexão de peering.

O WebRTC lida com NATs e firewalls com:

  • A estrutura ICE para estabelecer o melhor caminho de rede possível entre os participantes.
  • Servidores STUN para determinar um IP e uma porta acessíveis publicamente para cada peer.
  • Servidores TURN se a conexão direta falhar e for necessário retransmitir dados.

Para mais informações sobre como o WebRTC funciona com servidores para sinalização e rede, consulte WebRTC no mundo real: STUN, TURN e sinalização.

Os recursos

A API RTCDataChannel oferece suporte a um conjunto flexível de tipos de dados. A API foi projetada para imitar exatamente o WebSocket, e o RTCDataChannel é compatível com strings e alguns dos tipos binários em JavaScript, como Blob, ArrayBuffer e ArrayBufferView. Esses tipos podem ser úteis ao trabalhar com transferência de arquivos e jogos multijogador.

O RTCDataChannel pode funcionar no modo não confiável e não ordenado (análogo ao protocolo de datagramas do usuário ou UDP), no modo confiável e ordenado (análogo ao protocolo de controle de transmissão ou TCP) e em modos parcialmente confiáveis:

  • O modo confiável e ordenado garante a transmissão das mensagens e a ordem em que elas são entregues. Isso gera um overhead extra, o que pode deixar esse modo mais lento.
  • O modo não confiável e não ordenado não garante que todas as mensagens cheguem ao outro lado nem em que ordem elas chegam. Isso remove a sobrecarga, permitindo que esse modo funcione muito mais rápido.
  • O modo confiável parcial garante a transmissão da mensagem em uma condição específica, como um tempo limite de retransmissão ou uma quantidade máxima de retransmissões. A ordem das mensagens também pode ser configurada.

O desempenho dos dois primeiros modos é quase o mesmo quando não há perdas de pacotes. No entanto, no modo confiável e ordenado, um pacote perdido faz com que outros pacotes fiquem bloqueados atrás dele, e o pacote perdido pode ficar desatualizado quando for retransmitido e chegar. É claro que é possível usar vários canais de dados no mesmo app, cada um com semântica confiável ou não confiável.

Confira uma tabela útil do livro High Performance Browser Networking (em inglês) de Ilya Grigorik:

TCPUDPSCTP
ConfiabilidadeConfiávelInstávelConfigurável
EntregaPedido concluídoNão ordenadoConfigurável
TransmissãoOrientado a bytesOrientado a mensagensOrientado a mensagens
Controle de fluxoSimNãoSim
Controle de congestionamentoSimNãoSim

Em seguida, você vai aprender a configurar o RTCDataChannel para usar o modo confiável e ordenado ou não confiável e não ordenado.

Configurar canais de dados

Há várias demonstrações simples de RTCDataChannel on-line:

Nesses exemplos, o navegador faz uma conexão de peering com ele mesmo, cria um canal de dados e envia uma mensagem pela conexão de peering. Em seguida, ele cria um canal de dados e envia a mensagem pela conexão de mesmo nível. Por fim, sua mensagem aparece na caixa do outro lado da página.

O código para começar é curto:

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

O objeto dataChannel é criado com base em uma conexão de mesmo nível já estabelecida. Ele pode ser criado antes ou depois da ocorrência da sinalização. Em seguida, transmita um rótulo para distinguir esse canal de outros e um conjunto de configurações de configuração opcionais:

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

Também é possível adicionar uma opção maxRetransmits (o número de vezes para tentar antes de falhar), mas só é possível especificar maxRetransmits ou maxPacketLifeTime, não ambos. Para semântica UDP, defina maxRetransmits como 0 e ordered como false. Para mais informações, consulte estas RFCs da IETF: Protocolo de transmissão de controle de fluxo e Extensão de confiabilidade parcial do protocolo de transmissão de controle de fluxo.

  • ordered: se o canal de dados deve garantir a ordem ou não
  • maxPacketLifeTime: o tempo máximo para tentar retransmitir uma mensagem com falha.
  • maxRetransmits: o número máximo de tentativas de retransmissão de uma mensagem com falha.
  • protocol: permite o uso de um subprotocolo, que fornece metainformações para o app.
  • negotiated: se definido como "true", remove a configuração automática de um canal de dados no outro peer, oferecendo sua própria maneira de criar um canal de dados com o mesmo ID do outro lado.
  • id: permite fornecer seu próprio ID para o canal, que só pode ser usado em combinação com negotiated definido como true.

As únicas opções que a maioria das pessoas precisa usar são as três primeiras: ordered, maxPacketLifeTime e maxRetransmits. Com o SCTP (agora usado por todos os navegadores que oferecem suporte ao WebRTC), "confiável" e "ordenado" são verdadeiros por padrão. É recomendável usar unreliable e unordered se você quiser controle total da camada de app, mas, na maioria dos casos, a confiabilidade parcial é útil.

Assim como o WebSocket, o RTCDataChannel dispara eventos quando uma conexão é estabelecida, fechada ou apresenta erros, e quando recebe uma mensagem do outro peer.

É seguro?

A criptografia é obrigatória para todos os componentes do WebRTC. Com o RTCDataChannel, todos os dados são protegidos com o Datagram Transport Layer Security (DTLS). O DTLS é um derivado do SSL, o que significa que seus dados estarão tão seguros quanto em qualquer conexão padrão baseada em SSL. O DTLS é padronizado e integrado a todos os navegadores que oferecem suporte ao WebRTC. Para mais informações, consulte o wiki do Wireshark.

Mude sua forma de pensar sobre os dados

Trabalhar com grandes quantidades de dados pode ser um problema em JavaScript. Como os desenvolvedores do Sharefest apontaram, isso exigiu uma nova maneira de pensar sobre os dados. Se você estiver transferindo um arquivo maior do que a quantidade de memória disponível, pense em novas maneiras de salvar essas informações. É aqui que tecnologias como a API FileSystem entram em ação, como você verá a seguir.

Criar um app de compartilhamento de arquivos

Agora é possível criar um app da Web que pode compartilhar arquivos no navegador com o RTCDataChannel. A criação com base no RTCDataChannel significa que os dados do arquivo transferido são criptografados e não entram em contato com os servidores de um provedor de apps. Essa funcionalidade, combinada com a possibilidade de se conectar a vários clientes para um compartilhamento mais rápido, torna o compartilhamento de arquivos WebRTC um forte candidato para a Web.

São necessárias várias etapas para fazer uma transferência bem-sucedida:

  1. Leia um arquivo em JavaScript usando a API File.
  2. Faça uma conexão entre clientes com RTCPeerConnection.
  3. Crie um canal de dados entre clientes com RTCDataChannel.

Há vários pontos a serem considerados ao tentar enviar arquivos pelo RTCDataChannel:

  • Tamanho do arquivo:se o tamanho do arquivo for razoavelmente pequeno e puder ser armazenado e carregado como um Blob, você poderá carregar na memória usando a API File e enviar o arquivo por um canal confiável (mas lembre-se de que os navegadores impõem limites ao tamanho máximo de transferência). À medida que o tamanho do arquivo aumenta, as coisas ficam mais complicadas. Quando um mecanismo de divisão é necessário, os fragmentos de arquivo são carregados e enviados para outro peer, acompanhados de metadados chunkID para que o peer possa reconhecê-los. Nesse caso, também é necessário salvar os blocos primeiro no armazenamento off-line (por exemplo, usando a API FileSystem) e salvar no disco do usuário somente quando o arquivo estiver completo.
  • Tamanho do bloco:são os menores "átomos" de dados do seu app. A divisão em blocos é necessária porque atualmente há um limite de tamanho de envio, mas isso será corrigido em uma versão futura dos canais de dados. A recomendação atual para o tamanho máximo do bloco é de 64 KiB.

Depois que o arquivo for totalmente transferido para o outro lado, ele poderá ser baixado usando uma tag de âncora:

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

Esses apps de compartilhamento de arquivos no PubShare e no GitHub usam essa técnica. Ambos são de código aberto e oferecem uma boa base para um app de compartilhamento de arquivos baseado em RTCDataChannel.

O que você pode fazer?

RTCDataChannel abre as portas para novas maneiras de criar apps para compartilhamento de arquivos, jogos multiplayer e entrega de conteúdo.

  • Compartilhamento de arquivos ponto a ponto, conforme descrito anteriormente
  • Jogos multiplayer, combinados com outras tecnologias, como WebGL, como visto no BananaBread da Mozilla
  • Fornecimento de conteúdo reinventado pela PeerCDN, uma estrutura que fornece recursos da Web por comunicação de dados ponto a ponto.

Mude a forma de criar apps

Agora você pode oferecer apps mais envolventes usando conexões de alto desempenho e baixa latência com o RTCDataChannel. Frameworks, como PeerJS e o SDK WebRTC do PubNub, facilitam a implementação do RTCDataChannel, e a API agora tem amplo suporte em várias plataformas.

O surgimento do RTCDataChannel pode mudar sua forma de pensar sobre a transferência de dados no navegador.

Saiba mais