Um dos recursos mais legais do HTML5 é o WebSockets. Os WebSockets nos permitem comunicar com um servidor, sem a necessidade das tradicionais requisições AJAX.
Neste tutorial, vamos aprender muito sobre WebSockets. Construiremos um exemplo prático de cliente e servidor que se comunicam via WebSockets. Neste tutorial, usaremos Node.js para o servidor. Se você quiser usar WebSockets com PHP, confira nosso post complementar.
O que são WebSockets?
WebSockets é uma técnica para comunicação bidirecional sobre um soquete (TCP), um tipo de tecnologia push.
Por que precisamos de WebSockets?
WebSockets são amplamente utilizados quando um aplicativo não favorece votação longa. Com sondagem longa, o servidor envia constantemente respostas que podem ou não conter os dados necessários. Esses dados são necessários para manter a conexão atualizada e ativa. Quando um cliente recebe os dados, ele verifica e gera outra solicitação, se necessário. Obviamente, existem vários benefícios e casos de uso para pesquisas longas. Porém, a votação longa não é a melhor e mais sofisticada tecnologia para comunicação cliente-servidor. Se a conexão expirar, uma nova será necessária. Além disso, pesquisas longas podem resultar em uma utilização de recursos muito ruim.
Estas são duas das principais razões pelas quais um a tecnologia push era necessária. Como sugere o nome, o servidor envia dados, sempre que tem algo para o cliente. Isso significa que o cliente não precisa fazer solicitações periódicas.
Vamos começar nossa demonstração com o servidor web. Esta é uma demonstração simples que o ajudará a começar a usar toda a tecnologia.
1. Construindo o servidor Node.js
Etapa 1: escolha sua biblioteca
Quando se trata de Node.js, você tem uma dúzia de bibliotecas para escolher para construir o servidor. Minha favorita é a ws, uma biblioteca extremamente simples para construir servidores. A biblioteca nos permite criar servidores e clientes. Quão legal é isso?
Obviamente, o pré-requisito para usar o ws seria instalar o Node. Portanto, não se esqueça de baixar e instalar o Node.js em seu sistema.
Em seguida, você precisa instalar a biblioteca. Bem, esse é um trabalho muito simples onde você executa o seguinte comando:
npm install ws
Em seguida, abra o editor de código mais amado e crie um arquivo chamado index.js.
Etapa 2: criar o servidor WebSocket
Primeiras coisas primeiro, você precisa importar o ws
módulo. Isso é bastante simples. Você deve digitar a linha a seguir e isso garantirá a importação de ws para seu aplicativo.
const WebSocket = require('ws')
Em seguida, você deve criar um servidor de soquete da Web em uma porta. O cliente se comunicará com o servidor através desta porta. No nosso caso, o número da porta é 8081.
const wss = new WebSocket.Server({ port: 8081 })
Então, o que deve acontecer quando o cliente deseja se comunicar com o servidor? Precisamos anotar a lógica para isso. O módulo ws vem com vários eventos para cada uma das seguintes mudanças de estado:
open
close
message
connection
upgrade
ping
Além disso, o módulo vem com algumas funções para acionar e manipular os eventos acima:
on
send
onerror
Em nosso servidor simples, emitimos o connection
evento sempre que o cliente se conecta. Para emitir um dos eventos, podemos fazer uso do on
função.
wss.on('connection', function connection(ws) {});
Caso haja um erro, o onerror
função pode ser usada. Aqui está um breve exemplo de como essas funções podem ser usadas.
ws.on("close", () => { /* handle the event */ }); ws.onerror = function (error) { /* handle the error */ }
O esqueleto básico do nosso servidor WebSocket, que abre e fecha a porta 8081 para o cliente se conectar, é o seguinte:
const WebSocket = require('ws') const wss = new WebSocket.Server({ port: 8081 }) wss.on('connection', function connection(ws) { ws.on("close", () => { console.log("Client disconnected"); }); ws.onerror = function () { console.log("Some Error occurred"); } });
Passo 3: Recebendo uma Mensagem
Um servidor só pode realizar uma destas duas operações com um cliente: enviar uma mensagem ou receber uma mensagem. Primeiramente, vamos ver como receber uma mensagem de um cliente. Uma vez estabelecida a conexão cliente e servidor, o servidor pode receber uma mensagem usando o message
função.
A comunicação do Websocket sempre acontece com frames. Estes são fragmentos de dados, que são transferidos do cliente para o servidor ou vice-versa. E os fragmentos de dados podem estar nos seguintes formatos:
- Texto quadros: estes contêm dados na forma de texto bruto.
- Binário quadros de dados: estes contêm partes binárias de dados.
- Quadros de pingue-pongue: estes são usados principalmente para verificar se a conexão foi estabelecida entre o cliente e o servidor. O navegador geralmente assume a responsabilidade de responder a essas mensagens.
- Conexão fechada quadros: estes são usados para handshake o fechamento da conexão.
Quando o navegador fala com o servidor, o que acontece no nosso caso, são usados os quadros de texto ou binários. a .send()
funcionalidade do cliente tende a enviar mensagens como dados binários ou de texto. Isso inclui vários formatos como ArrayBuffer
e Blob
também. Você não precisa configurar nada para enviar dados em um formato específico. Basta enviá-lo e decodificá-lo no servidor.
Enquanto isso, se você quiser mencionar explicitamente o tipo de quadro, use o .binaryType
propriedade do soquete.
ws.binaryType = "arraybuffer"
Se você deseja decodificar os dados do buffer, para string, você pode usar a seguinte função. Aqui fazemos uso de Buffer.from()
Seguido por .toString()
para converter o buffer em string.
ws.on("message", (msg) => { var buf = Buffer.from(msg); console.log(buf.toString()); });
Passo 4: Enviando uma mensagem para o cliente
A próxima funcionalidade do servidor é enviar uma mensagem ao cliente. Para isso, utilizamos o ws.send()
função.
Agora que temos o básico do nosso servidor identificado, e definido, vamos construir a aplicação onde o servidor envia uma mensagem para o cliente, sempre que o cliente se conecta com ele. No trecho de código abaixo, o servidor continua a enviar 'hello world'
a cada segundo, até que o cliente se desconecte.
Assim é simples, enviar uma mensagem para o cliente.
const interval = setInterval(() => { ws.send('hello world') }, 1000)
Etapa 5: executando o servidor
Finalmente, você precisa executar o servidor. Para isso, você precisa abrir uma janela de terminal, digitar o seguinte comando e deixar o servidor rodando. Não se esqueça de abrir o terminal na pasta onde está o index.js criado na etapa 1 está presente.
node index.js
E, todo o código do servidor para conectar, desconectar, enviar e receber mensagens, aparece conforme abaixo:
const WebSocket = require('ws') const wss = new WebSocket.Server({ port: 8081 }) wss.on('connection', function connection(ws) { console.log('Client connected') const interval = setInterval(() => { ws.send('hello world') }, 1000) ws.on("close", () => { console.log("Client disconnected"); }); ws.onerror = function () { console.log("Some Error occurred"); } ws.on("message", (msg) => { var buf = Buffer.from(msg); console.log(buf.toString()); }); });
2. Construindo o Cliente
Agora que o servidor está funcionando, é hora de construir o cliente. Para isso, crie outro projeto Node com os seguintes arquivos:
node_modules index.html index.js package.json
Etapa 1: instalar a biblioteca
Assim como instalamos o ws para construir nosso servidor, você também deve instalar o ws no pacote do cliente. Sua pacote.jsondeve ter a seguinte dependência:
{ "dependencies": { "ws": "^8.8.1" } }
Etapa 2: Conectando-se ao servidor da Web
Para conectar com o servidor web, temos que inicializar um novo WebSocket. E o soquete da web precisa se conectar à porta definida durante a construção do servidor da web. No nosso caso, será novamente 8081.
var hostURL = "ws://localhost:8081"; webSocket = new WebSocket(hostURL);
Existem três partes importantes no URL do host.
- esquema: ws
- host: localhost
- porta: 8081
Com new WebSocket(url)
, a conexão acontece imediatamente. Durante o processo de estabelecimento da conexão, o cliente (também conhecido como navegador) usa cabeçalhos para perguntar ao servidor se WebSockets são suportados ou não. Se o servidor responder com a’yes, a conexão é estabelecida e os dois começam a conversar. O protocolo utilizado é o Protocolo WebSocket. Aqui nós não use o protocolo HTTP de forma alguma!
Aqui estão alguns dos cabeçalhos de solicitação, em nossa conexão cliente-servidor.
GET ws://localhost:8081/ HTTP/1.1 Host: localhost:8081 Connection: Upgrade . . Upgrade: websocket . Sec-WebSocket-Version: 13 . . Sec-WebSocket-Key: cBtV+sKFkk3wqmFFr909Vg== Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
-
Connection: upgrade
diz que o cliente precisa de uma mudança no protocolo. -
Upgrade: websocket
menciona que o protocolo solicitado é “websocket”. -
Sec-WebSocket-Key
é uma chave gerada pelo navegador. Este é um número aleatório que garante que o servidor suporte o protocolo WebSocket. Também evita que vários proxies armazenem em cache qualquer conversa feita entre o cliente e o servidor. -
Sec-WebSocket-Version
identifica a versão do protocolo WebSocket. O mais recente é 13.
Se o servidor concordou em estabelecer uma conexão, a resposta seria 101. E os cabeçalhos de resposta serão os seguintes:
101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: abcd=
Sec-WebSocket-Accept
tem uma chave gerada usando um algoritmo muito especial. No momento em que o navegador (também conhecido como cliente) percebe essa chave, ele sabe que o servidor realmente suporta o protocolo WebSocket. Doravante, os dados podem ser transferidos na forma de quadros.
Você não pode emular Handshakes WebSocket. você não pode usar buscar ou XMLHttpRequest para fazer uma solicitação HTTP. Por quê? Porque o JavaScript não tem permissão para definir os cabeçalhos mencionados acima.
Etapa 3: definir os eventos WebSocket
Antes de definirmos nossos eventos de web socket, você precisa ter um melhor entendimento sobre eles. Em nosso cliente, usaríamos três eventos diferentes:
-
onOpen
: Funcionalidade a ser acionada quando o soquete é aberto. -
onMessage
: Funcionalidade a ser acionada quando uma mensagem é recebida do servidor. -
onClose
: Funcionalidade a ser acionada quando o servidor é fechado.
Os eventos acima devem ser chamados usando o WebSocket
objeto.
webSocket.onopen = function(){} webSocket.onmessage = function(msg){} webSocket.onclose = function(){}
Em nossa demonstração, o cliente se conectará ao servidor e exibirá qualquer mensagem recebida do servidor na página HTML. Da mesma forma, qualquer alteração no estado de conectar para desconectar será impressa na página HTML. Para conseguir isso, vamos escrever um connect
método, que irá implementar os eventos Web Socket de acordo.
function connect(){ try{ webSocket = new WebSocket(hostURL); messageDiv.innerHTML = "Socket status:" + websocketReadyStateArray[webSocket.readyState] + "
"; webSocket.onopen = function(){ messageDiv.innerHTML += "Socket status:" + websocketReadyStateArray[webSocket.readyState] + "
"; connectBtn.disabled = true; disconnectBtn.disabled = false; } webSocket.onmessage = function(msg){ messageDiv.innerHTML += "Server response : " + msg.data + "
"; } webSocket.onclose = function(){ messageDiv.innerHTML += "Socket status:" + websocketReadyStateArray[webSocket.readyState] + "
"; connectBtn.disabled = false; disconnectBtn.disabled = true; } }catch(exception){ messageDiv.innerHTML += 'Exception happen, ' + exception; } }
Você pode ver que nossa função connect tem um bloco try/catch. É uma boa prática de codificação colocar toda a comunicação do servidor dentro do bloco try/catch. Se algo der errado, precisamos ter uma estratégia alternativa. E, try/catch oferece isso para nós! Ele fornece ao cliente uma maneira de informar ao usuário que algo deu errado com a conexão. Também é um ótimo lugar para depurar o fluxo geral.
No método acima, o onmessage
função assume o papel de imprimir qualquer mensagem do servidor na página HTML.
Passo 4: A Ponte
A função de conexão não inicia a conexão cliente-servidor imediatamente. Podemos ver que o websocket
está definido, mas a definição do messageDiv
, connectBtn
, disconnectBtn
e hostUrl
parece vir de outro lugar.
Dividimos o código do cliente em duas seções, init()
e connect()
. É responsabilidade do init
função para carregar todas essas variáveis. E a init
A função deve ser carregada quando a página for carregada. A definição do init
função é dada abaixo.
function init(){ messageDiv = document.getElementById("message"); textInput = document.getElementById("text"); hostURL = "ws://localhost:8081"; websocketReadyStateArray = new Array('Connecting', 'Connected', 'Closing', 'Closed'); connectBtn = document.getElementById('connect'); disconnectBtn = document.getElementById('disconnect'); connectBtn.disabled = false; sendTextBtn.disabled = true; sendJSONObjectBtn.disabled = true; disconnectBtn.disabled = true; }
Etapa 5: a página do cliente
A página do cliente é bastante simples e direta. Teremos o seguinte:
- uma
div
onde a mensagem do servidor será carregada - dois
button
s para conectar e desconectar do servidor
Aqui está um código HTML, para construir esta página. Ele precisa ser inserido index.html:
Html5 WebSockets Example Html5 WebSockets Example.
Assim que a conexão for estabelecida, o cliente receberá ‘Hello World’ do servidor. Agora, a página aparecerá como abaixo.
O pedaço de código completo no cliente index.js é o seguinte:
var webSocket; var messageDiv; var textInput; var hostURL; var websocketReadyStateArray; var connectBtn; var disconnectBtn; function init(){ messageDiv = document.getElementById("message"); textInput = document.getElementById("text"); hostURL = "ws://localhost:8081"; websocketReadyStateArray = new Array('Connecting', 'Connected', 'Closing', 'Closed'); connectBtn = document.getElementById('connect'); disconnectBtn = document.getElementById('disconnect'); connectBtn.disabled = false; sendTextBtn.disabled = true; sendJSONObjectBtn.disabled = true; disconnectBtn.disabled = true; } function connect(){ try{ webSocket = new WebSocket(hostURL); messageDiv.innerHTML = "Socket status:" + websocketReadyStateArray[webSocket.readyState] + "
"; webSocket.onopen = function(){ messageDiv.innerHTML += "Socket status:" + websocketReadyStateArray[webSocket.readyState] + "
"; connectBtn.disabled = true; disconnectBtn.disabled = false; } webSocket.onmessage = function(msg){ messageDiv.innerHTML += "Server response : " + msg.data + "
"; } webSocket.onclose = function(){ messageDiv.innerHTML += "Socket status:" + websocketReadyStateArray[webSocket.readyState] + "
"; connectBtn.disabled = false; disconnectBtn.disabled = true; } }catch(exception){ messageDiv.innerHTML += 'Exception happen, ' + exception; } } function selectAll(){ textInput.select(); } function disconnect(){ webSocket.close(); }
Etapa 6: enviando dados do cliente para o servidor
Até o trecho de código acima, você poderá enviar dados do servidor para o cliente. Para enviar dados do cliente para o servidor, você precisa implementar mais algumas linhas de código.
Vamos adicionar uma caixa de texto, em nossa página HTML, onde o usuário pode inserir dados. E um botão para enviar o conteúdo da caixa de texto para o servidor quando clicado.
Please input some text to Send :
Quando o botão é clicado, o seguinte método é invocado para enviar uma mensagem ao servidor. Como o quadro padrão do soquete da web é usado, a mensagem será enviada como um Buffer
.
function sendMessage(){ webSocket.send(textInput.value) }
Ao clicar Enviar mensagemo seguinte será exibido no console do servidor, conforme abaixo:
Etapa 7: executando o cliente
Para executar o cliente, você não precisa fazer muito. você precisa abrir index.html no seu navegador favorito e, em seguida, clique em Conectar para estabelecer a conexão com o servidor.
É isso!
Conclusão
Isso nos leva ao final do nosso tutorial sobre WebSockets usando HTML e Javascript (NodeJS). Os WebSockets são extremamente empolgantes e mudaram drasticamente nos últimos anos. WS é apenas uma das muitas bibliotecas de soquetes da Web para escolher. Você tem que fique de olho no W3C WebSocket API, para aprender tudo sobre as mais recentes mudanças nesta tecnologia PUSH.
WebSocket é uma maneira persistente de estabelecer uma conexão navegador-servidor. Eles não têm limitação de origem cruzada e são suportados em todos os navegadores modernos. A API é simples com métodos como .send
e .close
.