O problema: conexões cliente-servidor e servidor-cliente de baixa latência
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 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 "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 houver uma conexão com o servidor (quando o evento open
for disparado), poderemos começar a enviar dados ao servidor usando o método send('your message')
no objeto de conexão. Ele costumava aceitar apenas strings, mas agora também pode enviar mensagens binárias com as especificações mais recentes. 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 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
};
As extensões são outro recurso recém-adicionado do WebSocket. 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
Sendo um protocolo moderno, a comunicação de origem cruzada está integrada diretamente 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 tem um novo conjunto de problemas. No caso do WebSocket, é a compatibilidade com servidores proxy que media as 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 WebSocket. Alguns servidores proxy não gostam disso e abandonarão 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 :)
Usar WebSockets hoje
O WebSocket ainda é uma tecnologia recente e não foi 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 pilhas de servidores tradicionais, como LAMP, sejam projetadas 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 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
- Erlang
- Shirasu (em inglês)
- C++
- libwebsockets (link em inglês)
- .NET
Versões do protocolo
O protocolo com fio (um handshake e a transferência de dados entre o cliente e o servidor) do WebSocket agora se chama 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 pode 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 (em inglês)
- Pinte comigo
- Pixelatr (link em inglês)
- Tracejado
- Caça-palavras on-line multiplayer massivo
- Servidor de ping (usado nos exemplos acima)
- Exemplo de HTML5demos