O Node.js é uma plataforma de servidor. A principal tarefa do servidor é processar solicitações de clientes, em particular, de navegadores, da maneira mais rápida e eficiente possível. A oitava parte da tradução do tutorial Node.js. que publicamos hoje é sobre os protocolos HTTP e WebSocket.

[Aconselhamos a ler] Outras partes do cicloParte 1:
Informações gerais e introduçãoParte 2:
JavaScript, V8, alguns truques de desenvolvimentoParte 3:
Hospedagem, REPL, trabalho com o console, módulosParte 4:
arquivos npm, package.json e package-lock.jsonParte 5:
npm e npxParte 6:
loop de eventos, pilha de chamadas, temporizadoresParte 7:
Programação assíncronaParte 8:
Guia Node.js, Parte 8: Protocolos HTTP e WebSocketParte 9:
Guia Node.js, parte 9: trabalhando com o sistema de arquivosParte 10:
Guia do Node.js, Parte 10: Módulos padrão, fluxos, bancos de dados, NODE_ENVPDF completo do Guia Node.js. O que acontece ao fazer solicitações HTTP?
Vamos falar sobre como os navegadores fazem solicitações para servidores usando o protocolo HTTP / 1.1.
Se você já teve uma entrevista no campo de TI, poderá ser perguntado o que acontece quando você digita algo na barra de endereços do seu navegador e pressiona Enter. Talvez essa seja uma das perguntas mais populares que ocorrem nessas entrevistas. Qualquer pessoa que faça essas perguntas quer saber se você pode explicar alguns conceitos bastante simples e descobrir se você entende os princípios da Internet.
Esta questão aborda muitas tecnologias, para entender os princípios gerais dos quais significa entender como é construído um dos sistemas mais complexos já construídos pela humanidade, que abrange o mundo inteiro.
▍ protocolo HTTP
Os navegadores modernos são capazes de distinguir os URLs reais inseridos na barra de endereços das consultas de pesquisa, para o processamento do qual o mecanismo de pesquisa padrão geralmente é usado. Vamos falar sobre URLs. Se você inserir um endereço de site, como
flaviocopes.com
, na linha do navegador, o navegador converterá esse endereço no formato
http://flaviocopes.com
, com base na suposição de que o protocolo HTTP será usado para trocar dados com o recurso especificado. Observe que no Windows, o que falaremos aqui pode parecer um pouco diferente do que no macOS e Linux.
Phase fase de pesquisa de DNS
Portanto, o navegador, iniciando o trabalho de download de dados do endereço solicitado pelos usuários, executa a operação de Pesquisa de DNS (Pesquisa de DNS) para descobrir o endereço IP do servidor correspondente. Os nomes simbólicos dos recursos inseridos na barra de endereços são convenientes para as pessoas, mas o dispositivo da Internet implica a possibilidade de trocar dados entre computadores usando endereços IP, que são conjuntos de números como 222.324.3.1 (para IPv4).
Primeiro, descobrindo o endereço IP do servidor, o navegador examina o cache DNS local para verificar se um procedimento semelhante foi executado recentemente. No navegador Chrome, por exemplo, existe uma maneira conveniente de examinar o cache DNS inserindo o seguinte endereço na barra de endereços:
chrome://net-internals/#dns
.
Se nada puder ser encontrado no cache, o navegador utilizará a chamada de sistema
gethostbyname
do POSIX para descobrir o endereço IP do servidor.
▍ função gethostbyname
A função
gethostbyname
primeiro verifica o
hosts
, que, no macOS ou Linux, pode ser encontrado em
/etc/hosts
para descobrir se as informações locais podem ser encontradas ao descobrir o endereço do servidor.
Se local significa resolver a solicitação e descobrir o endereço IP do servidor, o sistema executará uma solicitação ao servidor DNS. Os endereços desses servidores são armazenados nas configurações do sistema.
Aqui estão alguns servidores DNS populares:
- 8.8.8.8: servidor DNS do Google.
- 1.1.1.1: servidor DNS CloudFlare.
A maioria das pessoas usa os servidores DNS fornecidos por seus provedores. O navegador executa consultas DNS usando o protocolo UDP.
TCP e UDP são dois protocolos básicos usados em redes de computadores. Eles estão localizados no mesmo nível conceitual, mas o TCP é um protocolo orientado a conexão e, para a troca de mensagens UDP, cujo processamento cria uma pequena carga adicional no sistema, não é necessário um procedimento de estabelecimento de conexão. Não falaremos exatamente sobre como os dados são trocados pelo UDP.
O endereço IP correspondente ao nome de domínio de seu interesse pode estar no cache do servidor DNS. Se não for esse o caso, ele entrará em contato com o servidor DNS raiz. O sistema do servidor DNS raiz consiste em 13 servidores, dos quais depende a operação de toda a Internet.
Deve-se notar que o servidor DNS raiz não conhece a correspondência entre todos os nomes de domínio e endereços IP existentes no mundo. Porém, servidores semelhantes conhecem os endereços dos servidores DNS de nível superior para domínios como .com, .it, .pizza e assim por diante.
Após o recebimento da solicitação, o servidor DNS raiz o redireciona para o servidor DNS do domínio de nível superior, para o chamado servidor TLD (do domínio de nível superior).
Suponha que o navegador esteja procurando o endereço IP do servidor
flaviocopes.com
. Voltando ao servidor DNS raiz, o navegador receberá o endereço do servidor TLD da zona .com. Agora, esse endereço será armazenado no cache; como resultado, se você precisar descobrir o endereço IP de outro URL da zona .com, não precisará acessar o servidor DNS raiz novamente.
Os servidores de TLD têm endereços IP de servidores de nomes (Name Server, NS), com a ajuda da qual você pode descobrir o endereço IP a partir do URL que temos. Onde o servidor NS obtém essas informações? O fato é que, se você compra um domínio, o registrador do domínio envia dados sobre ele para os servidores de nomes. Um procedimento semelhante é realizado, por exemplo, ao alterar a hospedagem.
Os servidores em questão geralmente são de propriedade de provedores de hospedagem. Como regra, para proteger contra falhas, vários desses servidores são criados. Por exemplo, eles podem ter estes endereços:
- ns1.dreamhost.com
- ns2.dreamhost.com
- ns3.dreamhost.com
Para descobrir o endereço IP por URL, no final, eles recorrem a esses servidores. Eles armazenam os dados reais sobre endereços IP.
Agora, depois que conseguimos descobrir o endereço IP por trás da URL inserida na barra de endereços do navegador, passamos para a próxima etapa do nosso trabalho.
▍ Estabelecendo uma conexão TCP
Depois de aprender o endereço IP do servidor, o cliente pode iniciar uma conexão TCP com ele. No processo de estabelecer uma conexão TCP, o cliente e o servidor transmitem um ao outro alguns dados de serviço, após o qual eles podem trocar informações. Isso significa que depois que a conexão for estabelecida, o cliente poderá enviar uma solicitação ao servidor.
RequestEnviando pedido
Uma solicitação é um fragmento de texto estruturado de acordo com as regras do protocolo usado. Consiste em três partes:
- String de consulta
- Cabeçalho da solicitação.
- Corpo da solicitação.
String de consulta
A cadeia de consulta é uma cadeia de texto única que contém as seguintes informações:
- Método HTTP.
- Endereço do recurso
- Versão do protocolo.
Pode parecer, por exemplo, assim:
GET / HTTP/1.1
Cabeçalho da solicitação
O cabeçalho da solicitação é representado por um conjunto de
:
. Existem 2 campos de cabeçalho obrigatórios, um dos quais é
Host
e o segundo é
Connection
. Os demais campos são opcionais.
O título pode ficar assim:
Host: flaviocopes.com Connection: close
O campo
Host
indica o nome de domínio no qual o navegador está interessado. O campo
Connection
, definido para
close
, significa que a conexão entre o cliente e o servidor não precisa ser mantida aberta.
Outros cabeçalhos de solicitação comumente usados incluem o seguinte:
Origin
Accept
Accept-Encoding
Cookie
Cache-Control
Dnt
De fato, existem muitos mais.
O cabeçalho da solicitação termina com uma sequência vazia.
Organismo de solicitação
O corpo da solicitação é opcional; não é usado nas solicitações GET. O corpo da solicitação é usado em solicitações POST, bem como em outras solicitações. Pode conter, por exemplo, dados no formato JSON.
Como agora estamos falando de uma solicitação GET, o corpo da solicitação estará vazio, não trabalharemos com ela.
▍ Resposta
Depois que o servidor recebe a solicitação enviada pelo cliente, ele a processa e envia uma resposta ao cliente.
A resposta começa com um código de status e uma mensagem correspondente. Se a solicitação for bem-sucedida, o início da resposta terá a seguinte aparência:
200 OK
Se algo der errado, pode haver outros códigos. Por exemplo, o seguinte:
404 Not Found
403 Forbidden
301 Moved Permanently
500 Internal Server Error
304 Not Modified
401 Unauthorized
Além disso, a resposta contém uma lista de cabeçalhos HTTP e o corpo da resposta (que, como a solicitação é executada pelo navegador, será código HTML).
Análise de HTML
Depois que o navegador recebe a resposta do servidor, cujo corpo contém código HTML, ele começa a analisá-lo, repetindo o processo acima para cada recurso necessário para formar a página. Esses recursos incluem, por exemplo, o seguinte:
- Arquivos CSS.
- Imagens
- Ícone de página da Web (favicon).
- Arquivos JavaScript.
Como exatamente o navegador exibe a página não se aplica à nossa conversa. O principal que nos interessa aqui é que o processo acima de solicitar e receber dados é usado não apenas para o código HTML, mas também para outros objetos transferidos do servidor para o navegador usando o protocolo HTTP.
Sobre a criação de um servidor simples usando o Node.js
Agora, depois de examinarmos o processo de interação entre o navegador e o servidor, você pode dar uma nova olhada na seção do aplicativo First Node.js da
primeira parte desta série de materiais, na qual descrevemos o código para um servidor simples.
Fazendo solicitações HTTP com Node.js
Para executar solicitações HTTP usando o Node.js, o
módulo apropriado
é usado . Os exemplos abaixo usam o módulo
https . O fato é que, em condições modernas, sempre que possível, é necessário usar o protocolo HTTPS.
▍ Executando solicitações GET
Aqui está um exemplo de execução de uma solicitação GET usando o Node.js:
const https = require('https') const options = { hostname: 'flaviocopes.com', port: 443, path: '/todos', method: 'GET' } const req = https.request(options, (res) => { console.log(`statusCode: ${res.statusCode}`) res.on('data', (d) => { process.stdout.write(d) }) }) req.on('error', (error) => { console.error(error) }) req.end()
Execução de solicitação POST
Veja como fazer uma solicitação POST no Node.js:
const https = require('https') const data = JSON.stringify({ todo: 'Buy the milk' }) const options = { hostname: 'flaviocopes.com', port: 443, path: '/todos', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': data.length } } const req = https.request(options, (res) => { console.log(`statusCode: ${res.statusCode}`) res.on('data', (d) => { process.stdout.write(d) }) }) req.on('error', (error) => { console.error(error) }) req.write(data) req.end()
▍PUT e DELETE consultas
A execução de tais solicitações é igual à execução de solicitações POST. A principal diferença, além do conteúdo semântico de tais operações, é o valor da propriedade
method
do objeto
options
.
▍ Executando solicitações HTTP no Node.js usando a biblioteca Axios
O Axios é uma biblioteca JavaScript muito popular que funciona tanto no navegador (que inclui todos os navegadores modernos e no IE, começando no IE8), quanto no ambiente Node.js., que pode ser usado para executar solicitações HTTP.
Essa biblioteca é baseada em promessas, possui algumas vantagens sobre os mecanismos padrão, em particular sobre a API Fetch. Entre suas vantagens estão as seguintes:
- Suporte para navegadores mais antigos (você precisa de um polyfill para usar o Fetch).
- Capacidade de interromper solicitações.
- Suporte para definir tempos limite para solicitações.
- Proteção integrada contra ataques CSRF.
- Suporte para upload de dados com o fornecimento de informações sobre o andamento desse processo.
- Suporte para conversão de dados JSON.
- Empregos em Node.js
Instalação
Você pode usar o npm para instalar o Axios:
npm install axios
O mesmo efeito pode ser alcançado com o fio:
yarn add axios
Você pode conectar a biblioteca à página usando
unpkg.com
:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
API do Axios
Você pode fazer uma solicitação HTTP usando o objeto
axios
:
axios({ url: 'https://dog.ceo/api/breeds/list/all', method: 'get', data: { foo: 'bar' } })
Mas geralmente é mais conveniente usar métodos especiais:
Isso é semelhante a como o jQuery usa
$.get()
e
$.post()
vez de
$.ajax()
$.post()
.
O Axios oferece métodos separados para executar outros tipos de solicitações HTTP, que não são tão populares quanto GET e POST, mas ainda são usados:
axios.delete()
axios.put()
axios.patch()
axios.options()
A biblioteca possui um método para executar uma solicitação projetada para receber apenas cabeçalhos HTTP, sem o corpo da resposta:
Solicitações GET
O Axios é conveniente de usar usando a sintaxe assíncrona / aguardada moderna. O exemplo de código a seguir, desenvolvido para o Node.js, usa a biblioteca para carregar uma lista de raças de
cães da API Dog . Aqui o método
axios.get()
é aplicado e as rochas são contadas:
const axios = require('axios') const getBreeds = async () => { try { return await axios.get('https://dog.ceo/api/breeds/list/all') } catch (error) { console.error(error) } } const countBreeds = async () => { const breeds = await getBreeds() if (breeds.data.message) { console.log(`Got ${Object.entries(breeds.data.message).length} breeds`) } } countBreeds()
O mesmo pode ser reescrito sem usar async / waitit, aplicando promessas:
const axios = require('axios') const getBreeds = () => { try { return axios.get('https://dog.ceo/api/breeds/list/all') } catch (error) { console.error(error) } } const countBreeds = async () => { const breeds = getBreeds() .then(response => { if (response.data.message) { console.log( `Got ${Object.entries(response.data.message).length} breeds` ) } }) .catch(error => { console.log(error) }) } countBreeds()
Usando parâmetros em solicitações GET
Uma solicitação GET pode conter parâmetros parecidos com este em um URL:
https:
Ao usar o Axios, uma consulta desse tipo pode ser feita assim:
axios.get('https:
O mesmo efeito pode ser alcançado configurando a propriedade
params
em um objeto com parâmetros:
axios.get('https://site.com/', { params: { foo: 'bar' } })
Solicitações POST
A execução de solicitações POST é muito semelhante à execução de solicitações GET, mas aqui, em vez do método
axios.get()
, o método
axios.post()
é usado:
axios.post('https:
Como segundo argumento, o método
post
aceita um objeto com parâmetros de solicitação:
axios.post('https://site.com/', { foo: 'bar' })
Usando o protocolo WebSocket no Node.js
O WebSocket é uma alternativa ao HTTP, pode ser usado para organizar a troca de dados em aplicativos da web. Esse protocolo permite criar canais de comunicação bidirecional de longa duração entre o cliente e o servidor. Após o estabelecimento da conexão, o canal de comunicação permanece aberto, o que coloca a aplicação à disposição de uma conexão muito rápida, caracterizada por baixas latências e uma pequena carga adicional no sistema.
O protocolo WebSocket é suportado por todos os navegadores modernos.
▍ Diferenças HTTP
HTTP e WebSocket são protocolos muito diferentes que usam abordagens diferentes para a troca de dados. O HTTP é baseado no modelo "solicitação-resposta": o servidor envia alguns dados para o cliente após a solicitação. No caso do WebSocket, tudo é organizado de maneira diferente. Ou seja:
- O servidor pode enviar mensagens para o cliente por sua própria iniciativa, sem aguardar uma solicitação do cliente.
- O cliente e o servidor podem trocar dados ao mesmo tempo.
- Ao transmitir uma mensagem, uma quantidade extremamente pequena de dados de serviço é usada. Isso, em particular, leva a baixa latência na transmissão de dados.
O protocolo WebSocket é muito adequado para comunicações em tempo real através de canais que permanecem abertos por muito tempo. O HTTP, por sua vez, é excelente para organizar sessões de comunicação ocasionais iniciadas pelo cliente. Ao mesmo tempo, deve-se notar que, do ponto de vista da programação, é muito mais fácil implementar a troca de dados usando o protocolo HTTP do que usando o protocolo WebSocket.
Version Versão protegida do protocolo WebSocket
Há uma versão não segura do protocolo WebSocket (esquema URI
ws://
), que se assemelha, em termos de segurança, ao protocolo
http://
. O uso de
ws://
deve ser evitado, preferindo uma versão segura do protocolo -
wss://
.
ReatCriando uma conexão WebSocket
Para criar uma conexão WebSocket, você precisa usar o
construtor apropriado:
const url = 'wss://myserver.com/something' const connection = new WebSocket(url)
Após uma conexão bem-sucedida, o evento
open
é gerado. Você pode organizar esse evento atribuindo uma função de retorno de chamada à propriedade
onopen
do objeto de
connection
:
connection.onopen = () => {
Para manipular erros, o manipulador de eventos
onerror
é usado:
connection.onerror = error => { console.log(`WebSocket error: ${error}`) }
DataEnviando dados para o servidor
Após abrir uma conexão WebSocket com o servidor, você pode enviar dados para ele. Isso pode ser feito, por exemplo, no
onopen
aberto:
connection.onopen = () => { connection.send('hey') }
▍ Obtendo dados do servidor
Para receber dados enviados usando o protocolo WebSocket do servidor, você pode atribuir o
onmessage
onmessage, que será chamado quando o evento da
message
for recebido:
connection.onmessage = e => { console.log(e.data) }
▍ Implementação do servidor WebSocket no ambiente Node.js.
Para implementar um servidor WebSocket no ambiente Node.js., você pode usar a popular biblioteca
ws . Vamos usá-lo para o desenvolvimento de servidores, mas é adequado para criar clientes, bem como para organizar a interação entre dois servidores.
Instale esta biblioteca inicializando o projeto:
yarn init yarn add ws
O código para o servidor WebSocket que precisamos escrever é bastante compacto:
constWebSocket = require('ws') const wss = newWebSocket.Server({ port: 8080 }) wss.on('connection', ws => { ws.on('message', message => { console.log(`Received message => ${message}`) }) ws.send('ho!') })
Aqui, criamos um novo servidor que escuta na porta padrão 8080 o protocolo WebSocket e descreve um retorno de chamada que, quando a conexão é estabelecida, envia uma mensagem
ho!
ao cliente
ho!
e imprime no console uma mensagem recebida do cliente.
Aqui está um exemplo de trabalho de um servidor WebSocket e
aqui está um cliente que pode interagir com ele.
Sumário
Hoje falamos sobre os mecanismos de rede suportados pela plataforma Node.js., traçando paralelos com mecanismos similares usados nos navegadores. Nosso próximo tópico estará trabalhando com arquivos.
Caros leitores! Você usa o protocolo WebSocket em seus aplicativos Web, cujo servidor foi criado usando o Node.js.?