Apresentando WebSockets: trazendo soquetes para a web

HTML5 Rocks

O problema: conexões de baixa latência de cliente-servidor e servidor-cliente

A web tem sido construída com base no conhecido paradigma de solicitação/resposta de HTTP. Um cliente carrega uma página da web e, em seguida, nada acontece até que o usuário clique na próxima página. Por volta de 2005, o AJAX começou a deixar a web mais dinâmica. Mesmo assim, toda a comunicação HTTP era direcionada pelo cliente, o que exigia interação do usuário ou sondagem periódica para carregar novos dados do servidor.

As tecnologias que permitem que o servidor envie dados ao cliente no mesmo momento em que constata que novos dados estão disponíveis foram usadas por algum tempo. Elas eram conhecidas por nomes como "Push" ou "Comet". Um dos problemas mais comuns para criar a ilusão de uma conexão iniciada pelo servidor é a chamada sondagem longa. Com a sondagem longa, o cliente abre uma conexão HTTP com o servidor que permanece aberta até que a resposta seja enviada. Sempre que tem novos dados, o servidor envia a resposta (outras técnicas envolvem Flash, solicitações XHR multipart e os chamados htmlfiles). A sondagem longa e as outras técnicas funcionam muito bem. Você as utiliza todos os dias em aplicativos como o chat do Gmail.

No entanto, todas essas soluções compartilham um problema: elas carregam a sobrecarga de HTTP, que não é adequada para aplicativos de baixa latência. Pense em jogos com vários jogadores no navegador ou em qualquer outro jogo on-line com um componente em tempo real.

Apresentando WebSocket: trazendo soquetes para a web

A especificação WebSocket define uma API que estabelece conexões de "soquete" entre um navegador da web e um servidor. Em outras palavras, há uma conexão persistente entre o cliente e o servidor e ambas as partes podem começar a enviar dados a qualquer momento.

Primeiros passos

Para abrir uma conexão WebSocket, basta chamar o construtor WebSocket:

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

Observe ws:. Há um novo esquema de URL para conexões WebSocket. Existe também wss: para uma conexão WebSocket segura e, do mesmo modo que https:, é usado para conexões HTTP seguras.

A associação imediata de alguns manipuladores de eventos à conexão permite identificar quando a conexão está aberta, quando há mensagens recebidas ou quando há um erro.

O segundo argumento aceita subprotocolos opcionais. Pode ser uma string ou uma matriz de strings. Cada string deve representar um nome de subprotocolo, e o servidor aceita somente um dos subprotocolos transmitidos na matriz. Para determinar o subprotocolo aceito, acesse a propriedade protocol do objeto WebSocket.

Os nomes de subprotocolos devem ser um dos nomes registrados no registro IANA. Atualmente, há somente um nome de subprotocolo (soap), registrado em fevereiro de 2012.

// When the connection is open, send some data to the server
connection.onopen = function () {
  connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
  console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
  console.log('Server: ' + e.data);
};

Comunicação com o servidor

Assim que houver uma conexão com o servidor (quando o evento open for acionado), poderemos começar a enviar dados para o servidor usando o método send('your message') no objeto de conexão. Ele é usado para suportar somente strings, mas, a partir da última especificação, agora também pode enviar mensagens binárias. Para enviar dados binários, use o objeto Blob ou ArrayBuffer.

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
  binary[i] = img.data[i];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

Da mesma forma, o servidor pode nos enviar mensagens a qualquer momento. Sempre que isso acontece, o retorno de chamada onmessage é acionado. O retorno de chamada recebe um objeto de evento, e a mensagem real pode ser acessada por meio da propriedade data.

WebSocket também pode receber mensagens binárias na última especificação. Os quadros binários podem ser recebidos no formato Blob ou ArrayBuffer. Para especificar o formato do binário recebido, defina a propriedade binaryType do objeto WebSocket como 'blob' ou 'arraybuffer'. O formato padrão é 'blob'. Não é necessário alinhar o parâmetro binaryType durante o envio.

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
  console.log(e.data.byteLength); // ArrayBuffer object if binary
};

As extensões são outro recurso recente do WebSocket. Usando as extensões, será possível enviar quadros compactados, multiplexados etc. Para localizar as extensões aceitas pelo servidor, examine a propriedade extensions do objeto WebSocket depois do evento open. Não há nenhuma especificação de extensão publicada oficialmente até fevereiro de 2012.

// Determining accepted extensions
console.log(connection.extensions);

Comunicação de origem cruzada

Sendo um protocolo moderno, a comunicação de origem cruzada está integrada diretamente no WebSocket. Embora você ainda deva se comunicar somente com clientes e servidores confiáveis, o WebSocket permite a comunicação entre partes de qualquer domínio. O servidor decide se disponibilizará seu serviço para todos os clientes ou somente para os que residem em um conjunto de domínios bem definidos.

Servidores proxy

Cada nova tecnologia tem um novo conjunto de problemas. No caso do WebSocket, é a compatibilidade com os servidores proxy que faz a mediação das conexões HTTP na maioria das redes corporativas. O protocolo WebSocket usa o sistema de upgrade HTTP (que normalmente é usado para HTTP/SSL) para fazer "upgrade" de uma conexão HTTP para uma conexão WebSocket. Alguns servidores proxy não gostam disso e abandonarão a conexão. Assim, mesmo se um determinado cliente usar o protocolo WebSocket, talvez não seja possível estabelecer uma conexão. Isso torna a próxima seção ainda mais importante :)

Use o WebSockets hoje mesmo

O WebSocket ainda é uma tecnologia jovem e não foi completamente implementada em todos os navegadores. No entanto, você pode usar o WebSocket hoje com as bibliotecas que usam um dos fallbacks mencionados acima sempre que o WebSocket não estiver disponível. Uma biblioteca que se tornou muito popular nesse domínio é a socket.io, que é fornecida com um cliente e uma implementação de servidor do protocolo e inclui fallbacks (o socket.io não oferece suporte para mensagens binárias ainda, segundo dados de fevereiro de 2012). Há também soluções comerciais como PusherApp que podem ser facilmente integradas em qualquer ambiente da web fornecendo uma API HTTP para enviar mensagens WebSocket aos clientes. Devido à solicitação HTTP extra, sempre haverá sobrecarga adicional em comparação com o WebSocket puro.

O lado do servidor

O uso de WebSocket cria um padrão de uso totalmente novo para aplicativos de servidor. Embora pilhas de servidor tradicionais como LAMP sejam desenvolvidas com base no ciclo de solicitação/resposta HTTP, elas geralmente não lidam bem com um grande número de conexões WebSocket abertas. Manter um grande número de conexões abertas ao mesmo tempo requer uma arquitetura que receba alta concorrência com baixo desempenho. Essas arquiteturas normalmente são desenvolvidas com base em encadeamento ou no chamado IO sem bloqueio.

Implementações de servidor

Versões de protocolo

O protocolo com fio (um handshake e a transferência de dados entre cliente e servidor) para WebSocket agora é RFC6455. As últimas versões do Google Chrome e do Google Chrome para Android são totalmente compatíveis com RFC6455, incluindo mensagens binárias. Além disso, o Firefox será compatível na versão 11 e o Internet Explorer na versão 10. Você ainda poderá usar versões mais antigas do protocolo, mas isso não é recomendado porque sabe-se que elas são vulneráveis. Se você tiver implementações de servidor para versões mais antigas do protocolo WebSocket, recomendamos que faça o upgrade para a versão mais recente.

Casos de uso

Use o WebSocket sempre que precisar de uma conexão quase em tempo real de baixa latência entre o cliente e o servidor. Tenha em mente que isso pode envolver a reformulação do modo como você cria os aplicativos de servidor com um novo foco em tecnologias como filas de eventos. Alguns exemplos de casos de uso:

  • Jogos on-line de vários jogadores
  • Aplicativos de chat
  • Links para esportes ao vivo
  • Atualização em tempo real de redes sociais

Demonstrações

Referências

Comments

0