Enviar dados entre navegadores com canais de dados WebRTC

O envio de dados entre dois navegadores para comunicação, jogos ou transferência de arquivos pode ser um processo bastante complexo. Ela exige a configuração e o pagamento de um servidor para retransmitir dados e talvez o escalonamento para vários data centers. Nesse cenário, há potencial para alta latência e é difícil manter a privacidade dos dados.

Esses problemas podem ser atenuados com o uso da API RTCDataChannel do WebRTC para transferência de dados diretamente de um ponto para outro. Este artigo aborda as noções básicas de como configurar e usar canais de dados, bem como os casos de uso comuns na Web atualmente.

Por que usar outro canal de dados?

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

RTCDataChannel adota uma abordagem diferente:

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

O RTCDataChannel agora é compatível com SCTP em computadores e dispositivos Android no Google Chrome, Opera e Firefox.

Uma ressalva: Signaling, STUN e TURN

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

O WebRTC lida com NATs e firewalls com:

  • O framework ICE (em inglês) para estabelecer o melhor caminho de rede possível entre pares.
  • Servidores STUN para verificar um IP e uma porta publicamente acessíveis para cada ponto.
  • 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 flaging (em inglês).

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 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 multiplayer.

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 nos modos parcialmente confiáveis:

  • O modo confiável e ordenado garante a transmissão de mensagens e a ordem em que elas são entregues. Isso leva a 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 para o outro lado nem em que ordem são recebidas. Isso remove a sobrecarga, permitindo que esse modo funcione muito mais rápido.
  • O modo parcialmente confiável garante a transmissão da mensagem em uma condição específica, como um tempo limite ou uma quantidade máxima de retransmissões. Também é possível configurar a ordem das mensagens.

O desempenho dos dois primeiros modos é quase o mesmo quando não há perda 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 obsoleto no momento em que é retransmitido e chega. Obviamente, é possível usar vários canais de dados no mesmo app, cada um com uma semântica própria ou não confiável.

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

TCPUDPSCTP
ConfiabilidadeConfiávelNão confiávelConfigurável
EntregaPedido concluídoNão ordenadaConfigurável
TransmissãoOrientada a bytesOrientada a mensagensOrientada 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.

Configuração de canais de dados

Existem várias demonstrações simples do RTCDataChannel on-line:

Nesses exemplos, o navegador faz uma conexão de peering consigo 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 peering. 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 peering já estabelecida. Pode ser criado antes ou depois da sinalização. Em seguida, você passa um rótulo para distinguir esse canal dos outros e um conjunto de definiçõ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, que é o número de tentativas antes de falhar. No entanto, só é possível especificar maxRetransmits ou maxPacketLifeTime, não ambos. Para semântica de UDP, defina maxRetransmits como 0 e ordered como false. Para mais informações, consulte estes RFCs do IETF: Stream Control Transmission Protocol e Stream Control Transmission Protocol Partial Reliability Extension (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 vezes para tentar retransmitir uma mensagem com falha.
  • protocol: permite o uso de um subprotocolo, que fornece metainformações para o app.
  • negotiated: se definido como verdadeiro, remove a configuração automática de um canal de dados no outro peering, fornecendo sua própria maneira de criar um canal de dados com o mesmo ID do outro lado.
  • id: permite que você forneça 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 compatíveis com WebRTC), a confiabilidade e a ordenação são verdadeiras por padrão. Faz sentido usar opções não confiáveis e não ordenadas se você quer 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, fechada ou apresenta erros, e quando recebe uma mensagem do outro peering.

É seguro?

A criptografia é obrigatória para todos os componentes WebRTC. Com RTCDataChannel, todos os dados são protegidos com o Datagram Transport Layer Security (DTLS, na sigla em inglês). O DTLS é derivado do SSL, o que significa que 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 WebRTC. Para mais informações, consulte a wiki do Wireshark.

Mude a forma como você pensa sobre os dados

Lidar com grandes quantidades de dados pode ser um problema no JavaScript. Como os desenvolvedores do Sharefest apontaram, isso exigia pensar sobre os dados de uma nova maneira. Se você estiver transferindo um arquivo que seja maior do que a quantidade de memória disponível, considere novas maneiras de salvar essas informações. É aqui que as tecnologias, como a API FileSystem, entram em ação, como você vai aprender 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. Criar sobre RTCDataChannel significa que os dados dos arquivos transferidos são criptografados e não afetam 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 de peering 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 no estado em que se encontra (embora os navegadores impõem limites para o tamanho máximo de transferência). Conforme o tamanho do arquivo aumenta, as coisas ficam mais complicadas. Quando um mecanismo de agrupamento é necessário, os blocos de arquivos são carregados e enviados a outro peering, acompanhados de metadados chunkID para que o peering possa reconhecê-los. Observe que, nesse caso, você também precisa salvar os blocos primeiro no armazenamento off-line (por exemplo, usando a API FileSystem) e salvá-los no disco do usuário somente quando tiver o arquivo inteiro.
  • Tamanho em partes:são os menores "átomos" de dados do app. A divisão é necessária porque no momento há um limite de tamanho de envio, embora isso seja corrigido em uma versão futura dos canais de dados. A recomendação atual para o tamanho máximo do bloco é de 64 KiB.

Quando o arquivo for totalmente transferido para o outro lado, o download poderá ser feito usando uma tag â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 têm 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 as portas para novas maneiras de criar apps para compartilhamento de arquivos, jogos multiplayer e envio de conteúdo.

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

Mude a maneira como você cria apps

Agora você pode oferecer apps mais envolventes usando conexões de alto desempenho e baixa latência pelo RTCDataChannel. Frameworks, como o PeerJS e o SDK WebRTC do PubNub (links em inglês), 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