WebSockets no Windows Consumer Preview
No Windows 8 Consumer Preview e Server Beta, o IE10 e todos os outros recursos de servidor e cliente WebSocket da Microsoft agora dão suporte à versão final do Protocolo WebSocket da IETF. Além disso, o IE10 implementa a recomendação candidata da API do WebSocket do W3C.
Os WebSockets estão estáveis e prontos para que os desenvolvedores comecem a criar serviços e aplicativos inovadores. Esta postagem apresenta uma introdução simples à API do WebSocket do W3C e ao seu protocolo WebSocket. A demonstração Flipbook atualizada usa a última versão da API e do protocolo.
Na minha postagem anterior, apresentei os cenários do WebSocket:
Os WebSockets permitem que aplicativos Web forneçam atualizações e notificações em tempo real no navegador. Os desenvolvedores têm enfrentado problemas para contornar as limitações do modelo HTTP de solicitação-resposta original que não foi criado para cenários em tempo real. Os WebSockets permitem que os navegadores abram um canal de comunicação full duplex bidirecional com os serviços. Cada lado, então, pode usar esse canal para enviar dados entre si imediatamente. Agora, sites de diferentes tipos, como redes sociais, de jogos e financeiros, podem oferecer melhores cenários em tempo real, usando, idealmente, a mesma marcação em diferentes navegadores.
Depois daquela postagem de setembro de 2011, os grupos de trabalho realizaram avanços significativos. O protocolo WebSocket agora é um protocolo padrão recomendado pela IETF. Além disso, a API do WebSocket é uma recomendação candidata do W3C.
Introdução à API do WebSocket usando um exemplo de eco
O trecho de código abaixo usa um servidor de eco simples criado com o namespace System.Web.WebSockets do ASP.NET para ecoar mensagens binárias e de texto que são enviadas pelo aplicativo. O aplicativo permite ao usuário digitar texto para ser enviado e retornado por eco como uma mensagem de texto ou desenhar uma imagem que pode ser enviada e retornada por eco como uma mensagem binária.
Para ver um exemplo mais complexo que lhe permita experimentar as diferenças na latência e no desempenho entre a sondagem do HTTP e de WebSockets, consulte a demonstração Flipbook.
Detalhes de conexão com um servidor WebSocket
Essa explicação simples se baseia em uma conexão direta entre o aplicativo e o servidor. Se um proxy estiver configurado, o IE10 inicia o processo enviando uma solicitação HTTP CONNECT para o proxy.
Quando um objeto WebSocket é criado, um handshake é trocado entre o cliente e o servidor para estabelecer a conexão WebSocket.
O IE10 inicia o processo enviando uma solicitação HTTP para o servidor:
GET /echo HTTP/1.1
Host: example.microsoft.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://microsoft.com
Sec-WebSocket-Version: 13
Vamos analisar cada parte dessa solicitação.
O processo de conexão é iniciado com uma solicitação HTTP GET padrão que permite que a solicitação percorra firewalls, proxies e outros intermediários:
GET /echo HTTP/1.1
Host: example.microsoft.com
O cabeçalho de atualização HTTP solicita que o servidor alterne o protocolo da camada de aplicativo de HTTP para o protocolo WebSocket.
Upgrade: websocket
Connection: Upgrade
O servidor transforma o valor no cabeçalho Sec-WebSocket-Key em sua resposta para demonstrar que ele compreende o protocolo WebSocket:
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
O cabeçalho de origem é definido pelo IE10 para permitir que o servidor cumpra a segurança baseada na origem.
Origin: http://microsoft.com
O cabeçalho Sec-WebSocket-Version identifica a versão do protocolo solicitado. A 13 é a versão final no padrão proposto pela IETF:
Sec-WebSocket-Version: 13
Se o servidor aceita a solicitação para atualizar o protocolo da camada de aplicativo, ele retorna a resposta de protocolos de alternância HTTP 101:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Para demonstrar que compreende o protocolo WebSocket, o servidor realiza uma transformação padronizada no Sec-WebSocket-Key da solicitação do cliente e retorna os resultados no cabeçalho Sec-WebSocket-Accept:
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
O IE10, então, compara o Sec-WebSocket-Key com o Sec-WebSocket-Accept para validar que o servidor é um WebSocket e não um HTTP com ilusões de grandeza.
O handshake do cliente estabeleceu uma conexão HTTP no TCP entre o IE10 e o servidor. Depois de o servidor retornar sua resposta 101, o protocolo da camada alterna de HTTP para WebSockets, que usa a conexão TCP estabelecida anteriormente.
Neste ponto, o HTTP está totalmente em segundo plano. Com o uso do protocolo de transferência leve WebSocket, as mensagens agora podem ser enviadas ou recebidas por um dos ponto de extremidade a qualquer momento.
Programando a conexão com um servidor WebSocket
O protocolo WebSocket define dois novos esquemas URI que são semelhantes aos esquemas HTTP.
- "ws:" "//" host [ ":" porta ] caminho [ "?" consulta ] é modelado no esquema “http:”. Sua porta padrão é a 80. É usado para conexões não protegidas (não criptografadas).
- "wss:" "//" host [ ":" porta ] caminho [ "?" consulta ] é modelado no esquema “https:”. Sua porta padrão é a 443. É usado para conexões seguras criadas no protocolo TLS.
Quando proxies ou intermediários de rede estão presentes, há uma maior probabilidade de êxito das conexões seguras, pois os intermediários ficam menos inclinados a tentar transformar o tráfego seguro.
O trecho de código a seguir estabelece uma conexão WebSocket:
var host = "ws://example.microsoft.com";
var socket = new WebSocket(host);
ReadyState – Preparar… Apontar… Fogo…
O atributo WebSocket.readyState representa o estado da conexão: CONNECTING, OPEN, CLOSING ou CLOSED. Quando o WebSocket é criado pela primeira vez, o readyState é definido como CONNECTING. Quando a conexão é estabelecida, o readyState é definido como OPEN. Se a conexão não for estabelecida, o readyState é definido como CLOSED.
Registrando-se em eventos de abertura
Para receber notificações quando a conexão for criada, o aplicativo precisa se registrar em eventos de abertura.
socket.onopen = function (openEvent) {
document.getElementById("serverStatus").innerHTML = 'Web Socket State::' + 'OPEN';
};
Detalhes sobre o envio e recebimento de mensagens
Após um handshake bem-sucedido, o aplicativo e o servidor Websocket podem trocar mensagens WebSocket. Uma mensagem é composta de uma sequência de um ou mais fragmentos de mensagem ou “quadros” de dados.
Cada quadro inclui informações como:
- Comprimento do quadro
- Tipo de mensagem (binária ou de texto) no primeiro quadro da mensagem
- Um sinalizador (FIN) indicando se é o último quadro da mensagem
O IE10 reagrupa os quadros em uma mensagem completa antes de passá-la para o script.
Programando o envio e recebimento de mensagens
A API send
permite aos aplicativos enviar mensagens para um servidor Websocket como texto UTF-8, ArrayBuffers ou Blobs.
Por exemplo, esse trecho recupera o texto inserido pelo usuário e envia-o para o servidor como mensagem de texto UTF-8 para ser retornada por eco. Ele verifica se o Websocket se encontra em um readyState OPEN:
function sendTextMessage() {
if (socket.readyState != WebSocket.OPEN)
return;
var e = document.getElementById("textmessage");
socket.send(e.value);
}
Esse trecho recupera a imagem desenhada pelo usuário em uma tela e envia-a para o servidor como uma mensagem binária:
function sendBinaryMessage() {
if (socket.readyState != WebSocket.OPEN)
return;
var sourceCanvas = document.getElementById('source');
// msToBlob returns a blob object from a canvas image or drawing
socket.send(sourceCanvas.msToBlob());
// ...
}
Registrando-se em eventos de mensagem
Para receber mensagens, o aplicativo deve se registrar em eventos de mensagem. O manipulador de eventos recebe um MessageEvent que contém os dados do MessageEvent.data. Os dados podem ser recebidos como mensagens de texto ou binárias.
Quando uma mensagem binária é recebida, o atributo WebSocket.binaryType controla se os dados da mensagem devem ser retornados como um Blob ou tipo de dados ArrayBuffer. O atributo pode ser definido como “blob” ou “arraybuffer”.
Os exemplos a seguir usam o valor padrão “blob”.
O trecho recebe a imagem ou texto retornado por eco do servidor websocket. Se os dados forem um Blob, a imagem foi retornada e é desenhada na tela de destino; caso contrário, uma mensagem de texto UTF-8 foi retornada e é exibida em um campo de texto.
socket.onmessage = function (messageEvent) {
if (messageEvent.data instanceof Blob) {
var destinationCanvas = document.getElementById('destination');
var destinationContext = destinationCanvas.getContext('2d');
var image = new Image();
image.onload = function () {
destinationContext.clearRect(0, 0, destinationCanvas.width, destinationCanvas.height);
destinationContext.drawImage(image, 0, 0);
}
image.src = URL.createObjectURL(messageEvent.data);
} else {
document.getElementById("textresponse").value = messageEvent.data;
}
};
Detalhes do fechamento de uma conexão WebSocket
Assim como há o handshake de abertura, há o handshake de fechamento. Qualquer ponto de extremidade (o aplicativo ou o servidor) pode iniciar um handshake.
Um tipo especial de quadro – um quadro de fechamento – é enviado para o outro ponto de extremidade. O quadro de fechamento pode conter um código de status opcional e o motivo do fechamento. O protocolo define um conjunto de valores apropriados para o código do status. O remetente do quadro de fechamento não pode enviar outros dados do aplicativo após o quadro de fechamento.
Quando o outro ponto de extremidade recebe o quadro de fechamento, ele responde com o seu próprio quadro de fechamento. Ele pode enviar mensagens pendentes antes de responder com o quadro de fechamento.
Programando o fechamento de um WebSocket e o registro em eventos de fechamento
O aplicativo inicia o handshake de fechamento em uma conexão aberta com a API close
:
socket.close(1000, "normal close");
Para receber notificações quando a conexão for fechada, o aplicativo precisa se registrar em eventos de fechamento.
socket.onclose = function (closeEvent) {
document.getElementById("serverStatus").innerHTML = 'Web Socket State::' + 'CLOSED';
};
A API close
aceita dois parâmetros opcionais: um código de status definido pelo protocolo e uma descrição. O código de status deve ser 1000 ou estar entre 3000 e 4999. Quando o fechamento é executado, o atributo readyState é definido como CLOSING. Depois que o IE10 recebe a resposta de fechamento do servidor, o atributo readyState é definido como CLOSED e um evento de fechamento é emitido.
Usando o Fiddler para ver o tráfego de WebSockets
O Fiddler é um proxy popular de depuração HTTP. Há suporte nas últimas versões do protocolo WebSocket. Você pode inspecionar os cabeçalhos trocados no handshake do WebSocket:
Todas as mensagens do WebSocket também são registradas. Na captura de tela abaixo, você pode ver que “spiral” foi enviado ao servidor como uma mensagem de texto UTF-8 e retornado por eco:
Conclusão
Se desejar saber mais sobre WebSockets, assista a estas sessões da conferência //Build/ da Microsoft de setembro de 2011:
- Building real-time Web apps with HTML5 WebSockets (Criando aplicativos Web em tempo real com WebSockets HTML5)
- Building real-time Web apps with WebSockets using IIS, ASP.NET and WCF (Criando aplicativos Web em tempo real com WebSockets usando IIS, ASP.NET e WCF)
- Building Windows runtime sockets apps (Criando aplicativos de soquetes em tempo de execução do Windows)
Se você estiver curioso sobre o uso de tecnologias da Microsoft para criar um serviço WebSocket, estas postagens serão uma ótima introdução:
- Getting started with WebSockets in the Windows 8 developer preview (Introdução aos WebSockets no Windows 8 developer preview)
- Getting to know System.Net.WebSockets: A simple ASP.NET echo server (Conhecendo o System.Net.WebSockets, um servidor de eco ASP.NET simples)
Comece a desenvolver usando WebSockets hoje e compartilhe os seus comentários conosco.
—Brian Raymor, gerente de programas sênior, Rede do Windows