O problema: conexões de baixa latência entre cliente e servidor
A Web foi construída principalmente em torno do chamado paradigma de solicitação/resposta do HTTP. Um cliente carrega uma página da Web e nada acontece até que o usuário clique na próxima página. Por volta de 2005, o AJAX começou a tornar a Web mais dinâmica. Ainda assim, toda a comunicação HTTP era direcionada pelo cliente, o que exigia a interação do usuário ou a pesquisa periódica para carregar novos dados do servidor.
As tecnologias que permitem que o servidor envie dados ao cliente no momento em que ele sabe que novos dados estão disponíveis já existem há algum tempo. Eles têm nomes como "Push" ou "Comet". Um dos hacks mais comuns para criar a ilusão de uma conexão iniciada pelo servidor é chamado de long polling. Com a long polling, o cliente abre uma conexão HTTP com o servidor, que é mantida aberta até o envio da resposta. Sempre que o servidor tem novos dados, ele envia a resposta. Outras técnicas envolvem Flash, solicitações XHR multipart e os chamados htmlfiles. A pesquisa longa e as outras técnicas funcionam muito bem. Você as usa todos os dias em aplicativos como o chat do Gmail.
No entanto, todas essas soluções alternativas têm um problema em comum: elas têm a sobrecarga do HTTP, o que não as torna adequadas para aplicativos de baixa latência. Pense em jogos de tiro em primeira pessoa multiplayer no navegador ou qualquer outro jogo on-line com um componente em tempo real.
Introdução ao WebSocket: trazendo soquetes para a Web
A especificação WebSocket define uma API que estabelece conexões "socket" entre um navegador da Web e um servidor. Em palavras simples: 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 o ws:
. Este é o novo esquema de URL para conexões WebSocket. Também há
wss:
para conexões WebSocket seguras, da mesma forma que https:
é usado para conexões HTTP
seguras.
Ao anexar alguns manipuladores de eventos imediatamente à conexão, você sabe quando a conexão é aberta, recebe mensagens ou há um erro.
O segundo argumento aceita subprotocolos opcionais. Pode ser uma string ou uma matriz de strings. Cada string precisa representar um nome de subprotocolo, e o servidor aceita apenas um dos subprotocolos transmitidos na matriz. O subprotocolo aceito pode ser determinado acessando a propriedade protocol
do objeto WebSocket.
Os nomes de subprotocolo precisam ser um dos nomes de subprotocolo registrados no registro da IANA. Atualmente, apenas um nome de subprotocolo (SOAP) está registrado desde 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);
};
Como se comunicar com o servidor
Assim que tivermos 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 só aceitava strings, mas na especificação mais recente, 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 enviar mensagens para nós a qualquer momento. Sempre que isso acontece, o callback onmessage
é acionado. O callback recebe um objeto de evento, e a mensagem real pode ser acessada pela propriedade data
.
O WebSocket também pode receber mensagens binárias na especificação mais recente. Os frames 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 ao enviar.
// 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
};
Outro recurso recém-adicionado do WebSocket são as extensões. Com as extensões, será possível enviar frames compactados, multiplexados etc. É possível encontrar extensões aceitas pelo servidor examinando a propriedade "extensions" do objeto WebSocket após o evento de abertura. Até fevereiro de 2012, ainda não havia uma especificação oficial de extensões publicada.
// Determining accepted extensions
console.log(connection.extensions);
Comunicação entre origens
Por ser um protocolo moderno, a comunicação entre origens é integrada ao WebSocket. Embora você ainda precise se comunicar apenas com clientes e servidores confiáveis, o WebSocket permite a comunicação entre as partes em qualquer domínio. O servidor decide se vai disponibilizar o serviço para todos os clientes ou apenas para aqueles que residem em um conjunto de domínios bem definidos.
Servidores proxy
Cada nova tecnologia traz um novo conjunto de problemas. No caso do WebSocket, é a compatibilidade com servidores proxy que mediam conexões HTTP na maioria das redes da empresa. 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 WebSocket. Alguns servidores proxy não gostam disso e descartam a conexão. Assim, mesmo que um determinado cliente use o protocolo WebSocket, talvez não seja possível estabelecer uma conexão. Isso torna a próxima seção ainda mais importante :)
Use WebSockets hoje mesmo
O WebSocket ainda é uma tecnologia recente e não está totalmente implementado em todos os navegadores. No entanto, você pode usar o WebSocket hoje com bibliotecas que usam uma das alternativas mencionadas acima sempre que o WebSocket não estiver disponível. Uma biblioteca que se tornou muito popular nesse domínio é a socket.io, que vem com uma implementação de cliente e servidor do protocolo e inclui substitutos (a socket.io ainda não oferece suporte a mensagens binárias em fevereiro de 2012). Também há soluções comerciais, como a PusherApp, que pode ser integrada facilmente a qualquer ambiente da Web, fornecendo uma API HTTP para enviar mensagens WebSocket aos clientes. Devido à solicitação HTTP extra, sempre haverá uma sobrecarga extra em comparação com o WebSocket puro.
O lado do servidor
O uso do WebSocket cria um novo padrão de uso para aplicativos do lado do servidor. Embora as pilhas de servidor tradicionais, como o LAMP, sejam projetadas em torno do 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 simultaneidade com um baixo custo de desempenho. Essas arquiteturas geralmente são projetadas em torno de linhas de execução ou de E/S não bloqueantes.
Implementações do lado do servidor
- Node.js
- Java
- Ruby
- Python
- pywebsocket (em inglês)
- Tornado
- Erlang
- C++
- .NET
Versões do protocolo
O protocolo de transmissão (um handshake e a transferência de dados entre cliente e servidor) para WebSocket agora é RFC6455. A versão mais recente do Chrome e do Chrome para Android são totalmente compatíveis com o RFC6455, incluindo mensagens binárias. Além disso, o Firefox será compatível com a versão 11 e o Internet Explorer com a versão 10. Ainda é possível usar versões mais antigas do protocolo, mas isso não é recomendado, já que elas são conhecidas por serem vulneráveis. Se você tiver implementações de servidor para versões mais antigas do protocolo WebSocket, recomendamos que você faça upgrade para a versão mais recente.
Casos de uso
Use o WebSocket sempre que precisar de uma latência realmente baixa e uma conexão quase em tempo real entre o cliente e o servidor. Isso pode envolver repensar como você cria seus aplicativos do lado do servidor com um novo foco em tecnologias, como filas de eventos. Alguns exemplos de casos de uso são:
- Jogos multiplayer on-line
- Aplicativos de chat
- Notícias esportivas ao vivo
- Atualização de streams sociais em tempo real
Demonstrações
- Plink (link em inglês)
- Paint With Me (em inglês)
- Pixelatr (link em inglês)
- Tracejado
- Caça-palavras on-line multiplayer massivo
- Servidor de ping (usado nos exemplos acima)
- Exemplo de HTML5demos