WebSocket simplificado

Mohd Shad Mirza Segue 3 de jul · 9 min ler

Este é o primeiro post da Série WebSocket que vou escrever, e o objetivo é explicar as coisas da maneira mais simples possível. Vamos pular direto para ele.

WebSockets permite que um usuário envie e receba mensagens para um servidor com uma conexão bidirecional persistente. Então, basicamente, esta é uma forma de comunicação entre o cliente e o servidor . Vamos entender essa comunicação primeiro, retornaremos ao WebSockets por um tempo.

Cliente e Servidor

Navegadores da Web (cliente) e servidores se comunicam via TCP / IP. O HTTP (Hypertext Transfer Protocol) é o protocolo de aplicativo padrão sobre solicitações de suporte TCP / IP (do navegador da Web) e suas respostas (do servidor).

Como é que isso funciona?

Vamos seguir estes passos simples: –

  1. O cliente envia uma solicitação ao servidor.
  2. Uma conexão é estabelecida.
  3. O servidor envia de volta a resposta.
  4. O cliente recebe a resposta.
  5. A conexão está fechada.

Esta é uma visão geral simples de como a comunicação padrão entre o cliente e o servidor funciona. Agora, dê uma olhada no passo no. 5

Conexão está fechada.

A solicitação HTTP cumpriu sua finalidade e não é mais necessária, portanto, a conexão é fechada.

E se o servidor quiser enviar uma mensagem ao cliente?

Em nosso cenário de solicitação / resposta padrão, a conexão deve ser estabelecida a partir da solicitação para iniciar a comunicação. Se o servidor quiser enviar uma mensagem, o cliente precisará enviar outra solicitação para estabelecer uma conexão e receber a mensagem.

Como o cliente saberá que o servidor deseja enviar uma mensagem?

Considere este exemplo:
O cliente está morrendo de fome e pediu comida on-line. Ele está fazendo uma solicitação por segundo para verificar se o pedido está pronto.

0 seg: a comida está pronta? (Cliente)
0 seg: não, espere. (Servidor)
1 seg: a comida está pronta? (Cliente)
1 seg: não, espere. (Servidor)
2 seg: a comida está pronta? (Cliente)
2 seg: não, espere. (Servidor)
3 seg: a comida está pronta? (Cliente)
3 seg: Sim senhor, aqui é o seu pedido. (Servidor)

Isto é o que você chama de HTTP Polling . O cliente faz solicitações repetidas ao servidor e verifica se há alguma mensagem a receber.

Como você pode ver, isso não é muito eficiente. Estamos usando recursos desnecessários e o número de solicitações com falha também é problemático.

Existe alguma maneira de superar este problema

Sim, há uma variação da técnica de pesquisa que é usada para superar essa deficiência e é chamada de Long-Polling .

Long Polling envolve basicamente fazer uma requisição HTTP para um servidor e então manter a conexão aberta para permitir que o servidor responda mais tarde (conforme determinado pelo servidor).

Considere a versão Long-Polling do exemplo acima:

0 seg: a comida está pronta? (Cliente)
3 seg: Sim senhor, aqui é o seu pedido. (Servidor)

Sim, problema resolvido.
Não exatamente. Embora Long Polling funcione, é muito caro em termos de CPU, memória e largura de banda ( já que estamos bloqueando recursos para manter a conexão aberta ).

O que fazemos agora? Parece que as coisas estão saindo do controle. Vamos voltar ao nosso salvador: WebSocket .

Por que WebSockets

Como você pode ver, Polling e Long-Polling são opções bastante caras para emular a comunicação em tempo real entre um cliente e um servidor. Esse gargalo de desempenho é o motivo pelo qual você deseja usar o WebSocket.

Os WebSockets não precisam que você envie uma solicitação para responder. Eles permitem o fluxo de dados bidirecional , então você só precisa ouvir qualquer dado.

Você pode apenas ouvir o servidor e ele enviará uma mensagem quando estiver disponível.

Vamos ver o lado do desempenho do WebSocket.

Consumo de recursos

O gráfico abaixo mostra as diferenças de consumo de largura de banda entre WebSockets vs Long Polling em um caso de uso relativamente comum:

A diferença é enorme e cresce exponencialmente com o número de solicitações.

Rapidez

Aqui estão os resultados para 1, 10 e 50 solicitações exibidas por conexão em um segundo:

Como você pode ver, fazer uma única solicitação por conexão é cerca de 50% mais lenta usando o Socket.io, já que a conexão deve ser estabelecida primeiro. Essa sobrecarga é menor, mas ainda perceptível para dez solicitações. Com 50 pedidos da mesma conexão, o Socket.io já é 50% mais rápido. Para ter uma ideia melhor do pico de processamento, vamos olhar para o benchmark com um número mais extenso (500, 1000 e 2000) de solicitações por conexão:

Aqui você pode ver que o benchmark HTTP atinge aproximadamente 950 pedidos por segundo, enquanto o Socket.io atende a cerca de 3900 solicitações por segundo. Eficaz, certo?

Nota: Socket.io é uma biblioteca JavaScript para aplicativos da Web em tempo real. Implementa o WebSocket internamente. Considere isso como um wrapper para WebSocket que fornece muitos mais recursos (o post do próximo blog desta série explica o Socket.io em detalhes) .

Como funcionam os WebSockets?

Estas são as etapas envolvidas no estabelecimento de uma conexão WebSocket.

  1. O cliente (navegador) envia uma solicitação HTTP ao servidor.
  2. Uma conexão é estabelecida através do protocolo HTTP.
  3. Se o servidor suportar o protocolo WebSocket, ele concorda em atualizar a conexão. Isso é chamado de aperto de mão.
  4. Agora que o handshake está concluído, a conexão HTTP inicial é substituída por uma conexão WebSocket que usa o mesmo protocolo TCP / IP subjacente.
  5. Neste ponto, os dados podem fluir livremente entre o Cliente e o Servidor.

Nós vamos criar dois arquivos: um servidor e um cliente.
Primeiro, crie um documento simples chamado <html> como client.html contendo uma tag <script> . Vamos ver como fica:

Client.html

 <html> 

<script>
// Nosso código vai aqui
</ script>

<body>
<h1> Esta é uma página do cliente </ h1>
</ body>

</ html>

Server.js

Agora crie outro arquivo server.js . Importe o módulo HTTP e crie um servidor. Faça ouvir a port 8000 .

Isso funcionará como um servidor http simples, escutando a port 8000 . Vamos ver isso também:

 // importando o módulo http 
const http = require ('http');

// criando um servidor http
servidor const = http.createServer ((req, res) => {
res.end ("Estou conectado");
});

// fazendo com que escute a porta 8000
server.listen (8000);

Execute o comando node server.js para iniciar a escuta da port 8000 . Você pode escolher qualquer porta que quiser, eu escolhi 8000 sem nenhuma razão específica.

Nossa configuração básica do cliente e do servidor é feita agora. Isso foi simples, certo? Vamos para as coisas boas agora.

Configuração do cliente

Para construir um WebSocket , use o construtor WebSocket() que retorna o objeto websocket . Esse objeto fornece a API para criar e gerenciar uma conexão WebSocket ao servidor .

Em palavras simples, este objeto websocket nos ajudará a estabelecer uma conexão com o servidor e criar um fluxo de dados bidirecional, ou seja, enviar e receber dados de ambas as extremidades .

Vamos ver como:

 <html> 

<script>
// chamando o construtor que nos fornece o objeto websocket: ws
vamos ws = new WebSocket ('url');
</ script>

<body>
<h1> Esta é uma página do cliente </ h1>
</ body>

</ html>

O construtor WebSocket espera uma URL para escutar. Que no nosso caso, é 'ws://localhost:8000' porque é onde o nosso servidor está rodando.
Agora, isso pode ser um pouco diferente do que você está acostumado. Nós não estamos usando o protocolo HTTP , estamos usando o protocolo WebSocket . Isso dirá ao cliente que 'Ei, estamos usando o protocolo websocket', portanto 'ws://' vez de 'http://' . Simples o suficiente? Agora vamos criar um servidor WebSocket no server.js .

Configuração do servidor

Vamos precisar de um módulo de terceiros ws em nosso servidor de nó para usar e configurar o servidor WebSocket .

Primeiro, vamos importar o módulo ws . Em seguida, vamos criar um servidor websocket e entregá-lo ao servidor HTTP escutando a port 8000 .

O servidor HTTP está atendendo à porta 8000 e o servidor WebSocket está escutando esse servidor HTTP. Basicamente, ele está ouvindo o ouvinte.

Agora nosso websocket está observando o tráfego na port 8000 . Isso significa que ele tentará estabelecer a conexão assim que o cliente estiver disponível. Nosso arquivo server.js ficará assim:

 const http = require ('http'); 
// importando o módulo ws
const websocket = require ('ws');

servidor const = http.createServer ((req, res) => {
res.end ("Estou conectado");
});
// criando servidor websocket
const wss = new websocket.Server ({servidor});

server.listen (8000);

Como discutimos anteriormente – o construtor WebSocket() retorna um objeto websocket que fornece a API para criar e gerenciar uma conexão WebSocket ao servidor .

Aqui, o objeto wss nos ajudará a ouvir o Event emitido quando certas coisas acontecem. Como a conexão é estabelecida ou o handshake está completo ou a conexão está fechada, etc.

Vamos ver como ouvir as mensagens:

 const http = require ('http'); 
const websocket = require ('ws');

servidor const = http.createServer ((req, res) => {
res.end ("Estou conectado");
});
const wss = new websocket.Server ({servidor});
// chamando um método 'on' que está disponível no objeto websocket
wss.on ('headers', (headers, req) => {
// registrando o cabeçalho
console.log (cabeçalhos);
});

server.listen (8000);

O método 'on' espera dois argumentos: Nome do evento e retorno de chamada. O nome do evento é o que queremos ouvir / emitir e o retorno de chamada especifica o que fazer com ele. Aqui, estamos apenas registrando o evento headers . Vamos ver o que temos:

Este é o nosso cabeçalho HTTP e eu quero que você seja curioso sobre isso, porque isso é exatamente o que está acontecendo nos bastidores. Vamos dividi-lo para entender melhor.

  • A primeira coisa que você notará é que obtivemos o código de status 101 . Você pode ter visto o código de status 200 , 201 , 404 , mas isso parece diferente. 101 é, na verdade, o código de status dos Protocolos de Comutação. Diz "Ei, eu quero atualizar" .
  • A segunda linha mostra as informações de atualização. Ele especifica que deseja atualizar para o protocolo websocket .
  • Isto é realmente o que acontece durante o aperto de mão. O navegador usa a conexão HTTP para estabelecer a conexão usando o HTTP/1.1 e, em seguida, faz o Upgrade para o protocolo websocket .

Agora isso fará mais sentido.

O evento Headers é emitido antes dos cabeçalhos de resposta serem gravados no soquete como parte do handshake. Isso permite que você inspecione / modifique os cabeçalhos antes que eles sejam enviados. Isso significa que você pode modificar os cabeçalhos para aceitar, rejeitar ou qualquer outra coisa que desejar. Por padrão, aceita a solicitação.

Da mesma forma, podemos adicionar mais uma connection evento que é emitida quando o handshake é concluído. Enviaremos uma mensagem ao cliente ao estabelecer uma conexão com sucesso. Vamos ver como:

 const http = require ('http'); 
const websocket = require ('ws');

servidor const = http.createServer ((req, res) => {
res.end ("Estou conectado");
});
const wss = new websocket.Server ({servidor});

wss.on ('headers', (headers, req) => {
//console.log (headers); Não registrando mais o cabeçalho
});

// Evento: 'conexão'
wss.on ('connection', (ws, req) => {
ws.send ('Esta é uma mensagem do servidor, conexão é estabelecida');
// recebe a mensagem do cliente no evento: 'message'
ws.on ('message', (msg) => {
console.log (msg);
});
});

server.listen (8000);

Também estamos ouvindo a message do evento vinda do cliente. Vamos criar isso:

 <html> 

<script>
vamos ws = new WebSocket ('url');
// registrando as propriedades da propriedade websocket
console.log (ws);
// enviando uma mensagem quando a conexão é aberta
ws.onopen = (event) => ws.send ("Esta é uma mensagem do cliente");
// recebendo a mensagem do servidor
ws.onmessage = (mensagem) => console.log (mensagem);
</ script>

<body>
<h1> Esta é uma página do cliente </ h1>
</ body>

</ html>

É assim que fica no navegador: –

O primeiro log é WebSocket listando todas as propriedades no objeto websocket e o segundo log é MessageEvent que possui uma propriedade data . Se você olhar de perto, verá que recebemos nossa mensagem do servidor.

O log do servidor será algo como isto:

Recebemos a mensagem do cliente corretamente. Isso marca que nossa conexão foi estabelecida com sucesso. Felicidades!

Conclusão

Para resumir, vamos ao que aprendemos:

  • Nós cobrimos como funciona o servidor HTTP, o que é Polling, Long Polling.
  • O que são WebSockets e porque precisamos deles.
  • Nós cobrimos como eles trabalham nos bastidores e entendemos melhor os cabeçalhos usados.
  • Criamos nosso próprio cliente e servidor e estabelecemos com sucesso a conexão entre eles.

Este é o básico do WebSockets e como eles funcionam. O próximo post da série abordará o socket.io e o socket.io com mais detalhes. Nós também veremos porque exatamente precisamos do socket.io quando as coisas estão funcionando bem com o único WebSocket() nativo WebSocket() . Por que usar uma biblioteca pesada quando conseguimos enviar e receber mensagens com sucesso?

Compartilhe a postagem se achar útil e fique ligado para a próxima.
Sável.

Referência