O que é TCP/IP?
TCP/IP é o conjunto de protocolos que sustenta a internet. O IP é responsável pelo endereçamento e roteamento de pacotes entre hosts; o TCP atua acima dele, transformando esses pacotes não confiáveis em um fluxo de bytes ordenado e confiável. Juntos, eles formam a camada de transporte sob o HTTP, bancos de dados, e-mail, WebSockets e quase tudo mais que seu servidor faz.
Raramente você escreverá código TCP diretamente, mas entendê-lo explica muita coisa: por que as conexões são caras, por que o keep-alive é importante, por que um pool de banco de dados tem um limite de tamanho e por que parte do tráfego utiliza UDP em vez disso.
O modelo de camadas
O networking geralmente é descrito em camadas, onde cada uma cuida de uma função específica:
| Camada | Função | Exemplos |
|---|---|---|
| Aplicação | Significado | HTTP, WebSocket, DNS |
| Transporte | Confiabilidade e portas | TCP, UDP |
| Internet | Endereçamento e roteamento | IP, ICMP |
| Link | Entrega local | Ethernet, Wi-Fi |
Cada camada utiliza a camada logo abaixo dela. O HTTP não sabe se está trafegando via Ethernet ou Wi-Fi; ele confia no TCP para entregar os bytes, que por sua vez confia no IP para mover os pacotes. Essa separação é o motivo pelo qual a internet consegue suportar tantos tipos diferentes de tráfego.
Endereços IP e portas
Um endereço IP identifica um host, e uma porta identifica um serviço nesse host. Juntos, eles formam um endereço de socket, como 203.0.113.10:443.
- Endereços IPv4 possuem 32 bits (
203.0.113.10); endereços IPv6 possuem 128 bits e são escritos em hexadecimal. - Portas abaixo de 1024 são reservadas para serviços conhecidos (well-known services).
- Clientes recebem uma porta efêmera durante o tempo de vida de uma conexão.
- Uma conexão é identificada de forma única pela tupla completa: endereço de origem, porta de origem, endereço de destino e porta de destino.
Essa tupla é o motivo pelo qual um servidor consegue aceitar milhares de conexões na porta 443 simultaneamente: a porta de origem de cada cliente é diferente.
O three-way handshake
Antes que qualquer dado flua, o TCP estabelece uma conexão em três etapas:
- O cliente envia um SYN com seu número de sequência inicial.
- O servidor responde com um SYN-ACK, confirmando o recebimento do cliente e enviando seu próprio número de sequência.
- O cliente envia um ACK, e a conexão é estabelecida.
Isso custa um round trip antes do primeiro byte de dados da aplicação, o que representa um custo real de latência. O TLS adiciona outro round trip (ou dois), e o DNS pode adicionar mais um antes disso. O reuso de conexão existe especificamente para evitar pagar esses custos repetidamente.
Confiabilidade e controle de fluxo
O TCP garante a confiabilidade por meio de alguns mecanismos:
- Números de sequência ordenam os bytes, permitindo que o receptor os remonte corretamente.
- Acknowledgements confirmam o recebimento, e dados não confirmados são retransmitidos.
- Checksums detectam corrupção de dados.
- Controle de fluxo utiliza uma janela de recebimento para que um remetente rápido não sobrecarregue um receptor lento.
- Controle de congestionamento adapta a taxa de envio à rede, evitando o colapso.
O resultado é um fluxo de bytes que chega completo e em ordem, ao custo de certa latência e overhead.
UDP: a alternativa rápida
O UDP não é orientado à conexão. Ele envia datagramas sem handshake, sem ordenação e sem retransmissão. Isso pode parecer pior, mas para algumas cargas de trabalho é a melhor opção:
- A latência importa mais do que a perfeição: vídeo ao vivo, voz e jogos preferem um frame perdido a um frame atrasado.
- A aplicação gerencia a confiabilidade: o QUIC, que alimenta o HTTP/3, constrói sua própria camada de confiabilidade sobre o UDP.
- Consultas pequenas e stateless: o DNS tradicionalmente usa UDP porque uma única requisição e resposta cabem em um único datagrama.
Escolha TCP quando a precisão e a ordenação forem fundamentais, e UDP quando você precisar de velocidade e puder tolerar perdas.
Sockets no Node.js
O Node expõe TCP através do node:net e UDP através do node:dgram. Um servidor TCP fornece a você um socket por conexão.
// echo.js
import { createServer } from "node:net";
const server = createServer((socket) => {
socket.setKeepAlive(true, 10_000);
socket.setTimeout(30_000);
socket.on("timeout", () => socket.destroy());
socket.on("data", (data) => socket.write(data));
socket.on("error", (err) => console.error(err));
});
server.listen(9000);
Sockets são streams: você lê eventos data e escreve com socket.write(). Como o TCP é um fluxo de bytes sem limites de mensagem, os protocolos de aplicação devem definir seu próprio enquadramento (framing), e é por isso que o HTTP possui headers com comprimentos de conteúdo e o WebSockets possui frames.
Ciclo de vida e ajuste de conexão
- Keep-alive reutiliza conexões, evitando o handshake em cada requisição.
- Pools limitam o número de conexões simultâneas a um banco de dados ou serviço.
- Timeouts fecham conexões ociosas ou travadas para que os recursos sejam liberados.
- TIME_WAIT é um estado normal após o fechamento, mas o excesso de conexões de curta duração pode esgotar as portas.
- Backpressure:
socket.write()retorna false quando o buffer está cheio, e você deve aguardar pordrain.
A maior parte do ajuste em produção consiste em reutilizar conexões e limitar a concorrência, e não em alterar parâmetros do kernel.
Melhores práticas
- Reutilize conexões com keep-alive ou um pool.
- Configure timeouts e keep-alive em conexões de longa duração.
- Limite a concorrência para não esgotar sockets ou descritores de arquivo (file descriptors).
- Trate erros e backpressure em cada socket.
- Use TCP, a menos que tenha um motivo específico para usar UDP.
- Entenda que o TCP é um fluxo de bytes (byte stream) e defina seu próprio framing.
- Monitore o TIME_WAIT e a contagem de conexões como um sinal de rotatividade de conexões (connection churn).
Erros comuns
- Abrir uma nova conexão para cada requisição.
- Presumir que o TCP preserva os limites das mensagens (message boundaries).
- Ignorar o backpressure de
socket.write()e fazer o buffering de dados ilimitados. - Manter conexões de longa duração sem keep-alive ou timeouts.
- Definir tamanhos de pool muito maiores do que o banco de dados consegue suportar.
- Optar por UDP quando a confiabilidade é realmente importante.
Próximos passos
O TCP/IP é a camada sobre a qual tudo mais é construído. Avance com o guia de HTTP, entenda os fundamentos de rede em Como a Internet Funciona e explore conexões persistentes com WebSockets. Depois, inspecione conexões reais na sua máquina com ss ou netstat enquanto seu servidor processa o tráfego.