Escrevendo aplicações cliente WebSocket

Aplicações cliente usam o WebSocket API para se comunicar com WebSocket servers sob o protocolo WebSocket.

Note: This feature is available in Web Workers.

Aviso: O fragmento de código neste artigo foi tomado de um exemplo de chat usando WebSocket. veja o código, então experimente o exemplo. O exemplo atual possui um bug; ele está tentando usar WebSockets inseguro e precisa ser atualizado para usar WebSocokets seguro. Iremos arrumar isso em breve!

Criando um objeto WebSocket

Para se comunicar utilizando o protocolo WebSocket, você precisa criar um objeto WebSocket, que automaticamente tentará abrir a conexão com o servidor.

O construtor WebSocket aceita dois campos, um obrigatório e um opcional:

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString protocols
);
url

A URL para se conectar. Esta deve ser a URL para qual o WebSocket irá responder.

protocols Optional

Uma única string indicando o protocolo ou uma array de strings de protocolos. Estas strings são usadas para indicar sub-protocolos, de forma que um único servidor pode implementar múltiplos sub-protocolos WebSocket (por exemplo, você pode querer que um servidor seja capaz de lidar com diferentes tipos de interações a depender do protocol especificado). Se não especificar uma string de protocolo, uma string vazia é assumida.

O construtor lançará a exceção SECURITY_ERR se o destino não permitir acesso. Isso pode acontecer se você tentar utilizar uma conexão insegura (a maioria dos user agents (en-US) agora necessitam de um link seguro para todas as conexões WebSocket, a menos que estejam no mesmo dispositivo ou na mesma rede).

Erros de Conexão

Se um erro ocorrer durante a tentativa de conexão, primeiro um simpes evento com o nome "error" é enviado ao objeto WebSocket (invocando, assim, seu manipulador onerror), e então o CloseEvent é enviado ao objeto WebSocket (invocando o manipulador onclose) para indicar a razão pela qual a conexão foi fechada.

O browser pode exibir uma descrição de erro mais detalhada na saída do console, ou mesmo um código de encerramento conforme definido na RFC 6455, Section 7.4 através do CloseEvent. Está implementado a partir do Firefox 11.

Exemplos

Neste simples exemplo, criaremos um novo WebSocket, conectando ao servidor em ws://www.example.com/socketserver. Neste exemplo utilizaremos um protocolo customizado denominado "protocolOne", embora possa ser omitido.

js
var exampleSocket = new WebSocket(
  "ws://www.example.com/socketserver",
  "protocolOne",
);

No retorno, exampleSocket.readyState está como CONNECTING. O readyState se tornará OPEN quando a conexão estiver pronta para transferir dados.

Se quiser abrir uma conexão e for flexível quanto aos protocolos suportados, você pode especificar um array de protocolos:

js
var exampleSocket = new WebSocket("ws://www.example.com/socketserver", [
  "protocolOne",
  "protocolTwo",
]);

Uma vez que a conexão for estabelecida (isso é, readyState está OPEN), exampleSocket.protocol informará qual protocolo o servidor selecionou.

Nos exemplos acima, ws foi substituído por http, de forma similar wss substitui https. Estabelecer uma conexão WebSocket depende do Mecanismo de Aprimoramento HTTP (en-US), de forma que o pedido para atualização de protocolo está implícito quando endereçamos o servidor HTTP como ws://www.example.com ou wss://www.example.com.

Enviando dados ao servidor

Uma vez a conexão aberta, você pode iniciar a transmisão de dados ao servidor. Para tanto, chame o método send() do WebSocket para cada mensagem que queira enviar:

js
exampleSocket.send(
  "Aqui vai algum texto que o servidor esteja aguardando urgentemente!",
);

Você pode enviar dados como uma string, Blob, ou um ArrayBuffer.

Nota: Nas versões anteriores à 11, o Firefox suporta apenas o envio de dados como string.

Visto que estabelecer uma conexão funciona de forma assícrona e, consequentemente, propensa a erros, não há garantia de sucesso ao chamar o método send() imediatamente após criar um objeto WebSocket. Podemos, pelo menos, ter certeza de que a tentativa de envio dos dados apenas ocorre quando uma conexão é estabelecida definindo um manipulador de eventos onopen:

js
exampleSocket.onopen = function (event) {
  exampleSocket.send(
    "Aqui vai algum texto que o servidor esteja aguardando urgentemente!",
  );
};

Utilizando JSON para transmitir objetos

Uma forma conveniente é usar JSON para enviar dados razoavelmente complexos ao servidor. Por exemplo, um aplicação de chat pode interagir com o servidor empregando um protocolo que utilize pacotes de dados JSON encapsulados:

js
// Enviar texto para todos os usuarios atraves do servidor
function sendText() {
  // Construir um objeto do tipo msg contendo o dado que o servidor precisa processar a partir do cliente de chat.
  var msg = {
    type: "message",
    text: document.getElementById("text").value,
    id: clientID,
    date: Date.now(),
  };

  // Enviar o objeto msg como um JSON em formato de string.
  exampleSocket.send(JSON.stringify(msg));

  // Esvaziar o campo input do elemento text, pronto pra receber a próxima linha de texto do usuário.
  document.getElementById("text").value = "";
}

Recebendo mensagens do servidor

A API WebSockets é dirigida por eventos (en-US); quando mensagens são recebidas, um evento de "mensagem" é entregue à função onmessage. Para começar a ouvir os dados de entrada, você pode fazer algo conforme o exemplo abaixo:

js
exampleSocket.onmessage = function (event) {
  console.log(event.data);
};

Recebendo e interpretando objetos JSON

Vamos considerar que a aplicação cliente de chat remete o envio de dados Utilizando JSON para transmitir objetos. Existem diversos tipos de pacotes de dados que o cliente pode receber, tais como:

  • Handshake de login
  • Messagem de texto
  • Atualizações da lista de usuários

O código que interpreta as mensagens de entrada se parecerá com esse:

js
exampleSocket.onmessage = function (event) {
  var f = document.getElementById("chatbox").contentDocument;
  var text = "";
  var msg = JSON.parse(event.data);
  var time = new Date(msg.date);
  var timeStr = time.toLocaleTimeString();

  switch (msg.type) {
    case "id":
      clientID = msg.id;
      setUsername();
      break;
    case "username":
      text =
        "<b>User <em>" +
        msg.name +
        "</em> signed in at " +
        timeStr +
        "</b><br>";
      break;
    case "message":
      text = "(" + timeStr + ") <b>" + msg.name + "</b>: " + msg.text + "<br>";
      break;
    case "rejectusername":
      text =
        "<b>Seu usuario foi configurado como <em>" +
        msg.name +
        "</em> porque o nome que você escolheu está em uso.</b><br>";
      break;
    case "userlist":
      var ul = "";
      for (i = 0; i < msg.users.length; i++) {
        ul += msg.users[i] + "<br>";
      }
      document.getElementById("userlistbox").innerHTML = ul;
      break;
  }

  if (text.length) {
    f.write(text);
    document.getElementById("chatbox").contentWindow.scrollByPages(1);
  }
};

Aqui utilizamos JSON.parse() para conveter o objeto JSON de volta ao objeto original, em seguida, examine e aja de acordo com seu conteúdo.

Formato de dados de texto

O formato de Texto recebido através de uma conexão WebSocket está no formato UTF-8.

Fechando a conexão

Quando finalizar o uso da conexão WebSocket, invoque o método close():

js
exampleSocket.close();

Pode ser útil examinar o atributo bufferedAmount do socket antes de tentar fechar a conexão para determinar se qualquer dado ainda está pendente de transmissão na rede.

Considerações de segurança

WebSockets não devem ser utilizados em um contexto de um ambiente misto, isto é, você não deveria abrir uma conexão não-segura a partir de uma página previamente carregada utilizando HTTPS, ou vice-versa. A maioria dos browsers atuamente apenas permitem conexões seguras pelo Websocket, e não mais suportam contextos diferentes desse.