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 transmitir dados e talvez dimensionar isso para vários data centers. Nesse cenário, há a possibilidade de latência alta e é difícil manter os dados privados.

Esses problemas podem ser atenuados com o uso da API RTCDataChannel do WebRTC para transferir dados diretamente de um ponto 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.

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 menos "saltos".
  • O RTCDataChannel usa o Protocolo de transmissão de controle de stream (SCTP, na sigla em inglês), permitindo entrega configurável fora de ordem e retransmissão da configuração semântica de entrega.

O RTCDataChannel já está disponível com suporte para SCTP no Google Chrome, Opera e Firefox para computador e Android.

Advertências: sinal, STUN e TURN

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

O WebRTC lida com NATs e firewalls com:

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

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 RTCDataChannel oferece suporte a strings e a 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 desordenado (análogo ao protocolo de datagramas do usuário (UDP), no modo confiável e ordenado (análogo ao TCP e ao protocolo de controle de transmissão)) e em modos confiáveis parciais:

  • O modo confiável e ordenado garante a transmissão de mensagens e a ordem em que elas são entregues. Isso leva a uma sobrecarga extra, o que pode tornar 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 a ordem em que 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 sob 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 é configurável.

O desempenho dos dois primeiros modos é praticamente 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 sejam bloqueados atrás dele, e o pacote perdido pode ficar desatualizado no momento em que é retransmitido e chega. É possível usar vários canais de dados no mesmo app, cada um com a própria semântica confiável ou não confiável.

Confira uma tabela útil de High Performance Browser Networking, de Ilya Grigorik:

TCPUDPSCTP
ConfiabilidadeConfiávelnão confiáveisConfigurável
EntregaPedido concluídoNão ordenadaConfigurável
TransmissãoOrientado a bytesOrientado a mensagensOrientado a mensagens
Controle de fluxoSimNãoSim
Controle de congestionamentoSimNãoSim

Em seguida, você vai aprender a configurar 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 peer consigo mesmo, cria um canal de dados e envia uma mensagem pela conexão de peer. Em seguida, ele cria um canal de dados e envia a mensagem pela conexão do par. Finalmente, 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 a partir de uma conexão de peer já estabelecida. Ele pode ser criado antes ou depois da sinalização. Em seguida, você transmite um rótulo para distinguir esse canal de outros e um conjunto de configurações 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 tentativas antes da falha), mas você só pode especificar maxRetransmits ou maxPacketLifeTime, não os dois. Para a semântica UDP, defina maxRetransmits como 0 e ordered como false. Para mais informações, consulte as RFCs da IETF: Protocolo de transmissão de controle de stream e Extensão de confiabilidade parcial do protocolo de transmissão de controle de stream.

  • ordered: se o canal de dados precisa 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 vezes para tentar retransmitir uma mensagem com falha
  • protocol: permite que um subprotocolo seja usado, o 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 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 com suporte ao WebRTC), a confiabilidade e a ordem são verdadeiras por padrão. Faz sentido usar a confiabilidade parcial e a ordem indefinida se você quiser ter controle total da camada do app, mas, na maioria dos casos, a confiabilidade parcial é útil.

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

É seguro?

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

Mude sua forma de pensar sobre os dados

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

Criar um app de compartilhamento de arquivos

Agora é possível criar um app da Web que compartilha arquivos no navegador com RTCDataChannel. Criar em cima de RTCDataChannel significa que os dados de arquivo transferidos são criptografados e não tocam os servidores de um provedor de app. Essa funcionalidade, combinada com a possibilidade de se conectar a vários clientes para compartilhamento mais rápido, faz do compartilhamento de arquivos do WebRTC um candidato forte para a Web.

Várias etapas são necessárias para fazer uma transferência:

  1. Leia um arquivo em JavaScript usando a API File.
  2. Estabeleça uma conexão ponto a ponto 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 por 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 como está (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 blocos de arquivo são carregados e enviados para outro peer, acompanhado de metadados chunkID para que o peering possa reconhecê-los. Nesse caso, você também precisa salvar os fragmentos primeiro no armazenamento off-line (por exemplo, usando a API FileSystem) e no disco do usuário somente quando tiver o arquivo completo.
  • Tamanho do bloco:são os menores "átomos" de dados do app. O agrupamento é necessário 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 é totalmente transferido para o outro lado, ele pode ser transferido 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.

Então, o que você pode fazer?

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

  • Compartilhamento de arquivos ponto a ponto, conforme descrito anteriormente
  • Jogos multiplayer, combinados com outras tecnologias, como o WebGL, como visto no BananaBread da Mozilla
  • O fornecimento de conteúdo está sendo reinventado pelo PeerCDN, um framework que entrega recursos da Web por meio de comunicação de dados ponto a ponto

Mudar a forma de criar apps

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

O advento do RTCDataChannel pode mudar a maneira como você pensa sobre a transferência de dados no navegador.

Saiba mais