MÓDULO 01 · CONCEITO 04 DE 8

Rede aplicada — TCP, HTTP, TLS, DNS

Você não precisa virar especialista em RFC. Mas precisa entender o suficiente para depurar quando latência aparecer.

Tempo de leitura ~24 min Pré-requisito I/O e syscalls Próximo Linux para devs

Quase todo problema de produção em sistemas distribuídos passa por rede em algum ponto. Latência subiu? Provavelmente alguma camada de rede mudou. Conexões resetando? Algum middleware está fechando antes do esperado. Certificado expirou? TLS quebrou. Resolução intermitente? DNS instável. A probabilidade de que sua próxima crise envolva rede é alta — e a diferença entre depurar em minutos ou em horas é exatamente quanto você sabe sobre o que está acontecendo embaixo das bibliotecas HTTP que você usa todo dia.

Este conceito não é um curso de redes — quem quer essa profundidade tem Computer Networks do Tanenbaum ou TCP/IP Illustrated do Stevens. O foco aqui é o vocabulário operacional que um engenheiro backend sênior precisa: como TCP estabelece e mantém conexões, o que mudou de HTTP/1.1 para HTTP/2 e HTTP/3, como funciona o handshake TLS e por que certificados expiram, e como DNS realmente resolve nomes — incluindo onde ele costuma falhar silenciosamente.

TCP — confiabilidade construída sobre IP não-confiável

A camada de rede (IP) entrega pacotes "best-effort": pacotes podem se perder, chegar fora de ordem, ou duplicar. TCP é a camada acima que transforma isso em fluxo confiável de bytes. As três propriedades garantidas por TCP — entrega, ordem, sem duplicação — são o que você assume quando escreve conn.Read(buf) em qualquer linguagem. Mas elas custam algo, e entender o custo é entender por que TCP às vezes parece "lento" sem motivo aparente.

O three-way handshake

Antes de qualquer dado, cliente e servidor trocam três mensagens para estabelecer a conexão:

  1. Cliente envia SYN (sincronize) com seu número de sequência inicial.
  2. Servidor responde SYN-ACK (sincronize-acknowledge) com seu número de sequência e o ACK do cliente.
  3. Cliente envia ACK finalizando o estabelecimento.

Esse handshake custa um round-trip completo (RTT) — tempo de ida e volta cliente-servidor. Em redes locais, é menos de 1ms; entre data centers próximos, ~10ms; transcontinental, ~150ms; satélite, ~600ms. Esse é o custo mínimo de qualquer conexão TCP nova, antes mesmo de transmitir um byte de dados. Por isso reuso de conexões (keep-alive, connection pooling) é tão importante: evita pagar handshake repetidamente.

Slow start e congestion control

TCP não envia dados na velocidade máxima imediatamente. Começa devagar, acelera enquanto detecta sucesso, e desacelera quando detecta perda. Esse é o mecanismo de congestion control — TCP cooperando com a rede compartilhada para não saturá-la. Algoritmos: Tahoe (1988), Reno, NewReno, CUBIC (default no Linux desde 2006), BBR (Google, 2016, cada vez mais adotado).

A consequência prática é que conexões novas têm throughput menor que conexões antigas. Uma conexão que existe há minutos e já "treinou" o congestion window pode transmitir muito mais por segundo que uma recém-aberta. Esse é outro motivo para reusar conexões — não só evita o handshake, evita o slow start.

Termino — FIN, RST, e o problema de TIME_WAIT

Conexões TCP terminam ordenadamente via troca de FIN, ou abruptamente via RST. O encerramento ordenado deixa o socket em estado TIME_WAIT por ~60 segundos (em Linux), durante o qual ele não pode ser reutilizado para a mesma quádrupla (IP origem, porta origem, IP destino, porta destino).

Em servidores que recebem muitas conexões curtas (proxy reverso, load balancer), TIME_WAIT pode acumular dezenas de milhares de sockets em estado pendente — e o servidor "esgota portas efêmeras" mesmo com tráfego baixo. Sintomas: novas conexões falhando com "address already in use", ss -s mostrando milhares de TIME_WAIT. Soluções: keep-alive (evita criar tantas conexões), tuning de net.ipv4.ip_local_port_range e net.ipv4.tcp_tw_reuse em Linux. Detalhes que entram em runbooks de SRE.

HTTP — três versões, três modelos diferentes

HTTP/1.1 — uma requisição por vez por conexão

HTTP/1.1 (RFC 2616, 1999) é texto sobre TCP. Você abre conexão, escreve GET /path HTTP/1.1\r\nHost: ...\r\n\r\n, lê resposta, e ou fecha ou envia próxima requisição. A limitação fundamental: cada conexão processa uma requisição por vez. Pipelining (enviar várias sem esperar respostas) existia mas era pouco implementado e tinha problemas.

Para paralelismo, browsers abriam múltiplas conexões TCP em paralelo (tipicamente 6 por origem). Isso desperdiçava recursos, mas funcionava. Em sites com muitos assets (CSS, JS, imagens), era a única forma de não serializar tudo. Por anos, essa limitação moldou como front-ends eram construídos (sprite sheets, concatenação de JS, asset domains).

HTTP/2 — multiplexing sobre uma única conexão

HTTP/2 (RFC 7540, 2015) resolveu o problema fundamental: múltiplas requisições em paralelo sobre uma única conexão TCP. Bytes são organizados em "frames" e "streams"; cada requisição é um stream que pode estar sendo transmitido entrelaçado com outros. Tem também:

HTTP/2 quase sempre roda sobre TLS na prática (HTTP/2 cleartext existe mas é raro), o que casa com o desejo da indústria de mover tudo para HTTPS. Hoje é universalmente suportado: virtualmente todo browser, todo servidor moderno (nginx, Apache, IIS, Caddy), todo client HTTP.

HTTP/3 — sobre QUIC, sobre UDP

HTTP/2 ainda sofre de um problema chamado head-of-line blocking no nível TCP: se um pacote TCP se perde, todos os streams sobre aquela conexão pausam até retransmissão, mesmo que apenas um deles dependa do pacote perdido. TCP não sabe da existência de streams.

HTTP/3 (RFC 9114, 2022) resolve isso movendo para QUIC — um protocolo de transporte construído sobre UDP, com criptografia integrada (TLS 1.3 dentro do próprio protocolo) e streams independentes. Cada stream tem suas próprias garantias; perda em um não afeta outros. Em redes ruins (mobile, alta latência, alta perda), HTTP/3 ganha visivelmente. Em LAN, a diferença é negligenciável.

Adoção atual: cerca de 30% do tráfego web global é HTTP/3 (2025). Cloudflare, Google, Meta operam pesado nele. Para APIs internas em data centers, HTTP/2 ainda domina por inércia e simplicidade. Para clientes móveis ou globais, HTTP/3 vira diferencial.

TLS — criptografia em transit

TLS (Transport Layer Security) é a evolução do SSL (que tem o nome preservado em "SSL certificates" por inércia). Versão atual padrão é TLS 1.3 (RFC 8446, 2018). TLS dá três coisas: confidencialidade (terceiros não leem), integridade (terceiros não modificam sem detectar), e autenticação (você sabe que está falando com quem disse que estava falando).

O handshake TLS

TLS 1.2 levava 2 RTTs para handshake; TLS 1.3 reduziu para 1 RTT (e tem modo 0-RTT para reconexões, com trade-offs de segurança). Em alto nível, cliente e servidor:

  1. Negociam versão TLS, ciphers suportadas e extensões.
  2. Servidor envia certificado (cadeia de certificados, na verdade).
  3. Cliente valida o certificado: assinatura, validade, hostname, cadeia até CA confiável.
  4. Acordam material criptográfico via troca Diffie-Hellman.
  5. Trocam dados criptografados.

TLS 1.3 também eliminou ciphers obsoletas (RC4, 3DES, MD5), forçou forward secrecy, e simplificou o protocolo. Para você como engineering: TLS 1.3 é o default desejável; bloquear TLS 1.0 e 1.1 está virando padrão da indústria; TLS 1.2 ainda existe por compatibilidade.

Certificados e CAs

O modelo de certificados X.509 é hierárquico: certificados raiz de Certificate Authorities (CAs) são pré-instalados em sistemas operacionais e browsers (Mozilla CA Bundle é referência); CAs assinam certificados intermediários; intermediários assinam certificados de servidores. Quando seu navegador valida um certificado, ele segue essa cadeia até chegar a uma CA confiável.

Pontos práticos onde isso costuma falhar:

armadilha em produção

"Funciona no curl mas não no app" frequentemente é cadeia de certificados incompleta. Use openssl s_client -connect host:443 -showcerts para inspecionar o que o servidor manda. Se faltam intermediários, configure no servidor (em Nginx: ssl_certificate deve incluir certificado + intermediários concatenados em PEM).

DNS — onde nomes viram IPs

DNS (Domain Name System) traduz nomes (api.example.com) em IPs (93.184.215.123). É hierárquico (root → TLD → autoritativo) e cacheável em múltiplas camadas. A resolução típica:

  1. App pede ao resolver local (libc, runtime).
  2. Resolver local consulta cache local (do app, do SO).
  3. Se miss, consulta resolver da máquina (em /etc/resolv.conf).
  4. Resolver vai para servidor DNS (geralmente do ISP ou público — 8.8.8.8, 1.1.1.1).
  5. Servidor DNS recursivo navega a hierarquia, cacheia, devolve resposta.

Cada nível tem TTL (time-to-live) próprio. Mudanças em DNS demoram a propagar — pelo TTL configurado, somado a caches em vários níveis. Quando um time muda IP de um serviço e "ainda vejo o IP antigo", quase sempre é cache em alguma camada.

Tipos de registros que importam

Onde DNS falha em produção

DNS é uma das fontes de problemas mais comuns e mais subestimadas em sistemas distribuídos. Padrões clássicos:

Ferramentas para depurar rede

O kit mínimo de quem trabalha com sistemas distribuídos:

Aprender o uso básico dessas ferramentas economiza horas em incidentes. Quando algo está estranho na rede, alguém vai precisar usá-las — e quem sabe vira o herói; quem não sabe vira espectador.

Como cada linguagem expõe rede

C# — HttpClient e Sockets
// HttpClient é o padrão moderno (não use WebClient).
// Importante: HttpClient deveria ser singleton ou via IHttpClientFactory
// — instâncias novas a cada request leak sockets em TIME_WAIT.

using var http = new HttpClient();
var response = await http.GetAsync("https://api.example.com/users");

// Para baixo nível, Socket:
using var sock = new Socket(AddressFamily.InterNetwork,
                            SocketType.Stream, ProtocolType.Tcp);
await sock.ConnectAsync("example.com", 443);
// ... TLS via SslStream se quiser handshake manual

.NET 10 tem suporte nativo a HTTP/3 via QUIC (System.Net.Quic). IHttpClientFactory resolve o problema de sockets vazados.

Python — httpx, aiohttp, sockets
# httpx é o cliente moderno (sucessor de requests, com async).
import httpx

async with httpx.AsyncClient() as client:
    r = await client.get("https://api.example.com/users")

# Para baixo nível, socket stdlib
import socket, ssl

ctx = ssl.create_default_context()
with socket.create_connection(("example.com", 443)) as sock:
    with ctx.wrap_socket(sock, server_hostname="example.com") as ssock:
        ssock.send(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
        print(ssock.recv(4096))

httpx suporta HTTP/2 nativamente (com httpx[http2]). HTTP/3 via libraries específicas (aioquic).

Go — net/http (stdlib)
// stdlib net/http é production-grade. Servidor e cliente.
import "net/http"

resp, err := http.Get("https://api.example.com/users")
if err != nil { return err }
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)

// Para baixo nível, package net:
conn, err := net.Dial("tcp", "example.com:443")
defer conn.Close()
// + crypto/tls.Client para TLS

Go 1.21+ tem suporte experimental a HTTP/3 via golang.org/x/net/http3. Stdlib aceita TLS 1.3 por default. Importante: sempre defer resp.Body.Close() — esquecer vaza connection pool.

Como praticar

  1. Inspecione um handshake TLS completo. Rode openssl s_client -connect google.com:443 -showcerts. Veja a cadeia de certificados, ciphers negociadas, versão TLS. Tente forçar TLS 1.2 com -tls1_2 e veja a diferença.
  2. Resolva DNS na mão. dig +trace example.com mostra a navegação root → TLD → autoritativo. Veja TTLs. Compare com dig +short que pula direto para o resultado cached.
  3. Compare HTTP/1.1 vs HTTP/2 vs HTTP/3. Use curl --http1.1 -w "%{http_version}\n" -o /dev/null https://... ; depois --http2, depois --http3. Compare também tempo total e número de conexões via curl -v --trace-time.

Referências para aprofundar

  1. livro TCP/IP Illustrated, Vol. 1: The Protocols — W. Richard Stevens (2nd ed., Fall 2011, Wright & Stevens). Bíblia técnica de TCP/IP. Capítulos sobre TCP (3-way, retransmission, congestion) são canônicos. Densidade alta.
  2. livro Computer Networking: A Top-Down Approach (8th ed.) — Kurose & Ross (2020). Curso universitário moderno. Top-down faz mais sentido para engenheiro de aplicação. Capítulos 2 (HTTP) e 3 (TCP) são suficientes.
  3. livro High Performance Browser Networking — Ilya Grigorik (2013). hpbn.co — gratuito online. Foco em web e mobile. Capítulos sobre HTTP/1, HTTP/2, e TLS são os melhores que existem para devs.
  4. artigo How to Read RFCs — IETF. ietf.org/standards/rfcs/ — quando você for ler RFC 9110 (HTTP semantics), saber a estrutura ajuda muito.
  5. artigo The QUIC Transport Protocol — Cloudflare blog. blog.cloudflare.com/the-road-to-quic — explicação acessível por quem opera QUIC em escala global.
  6. artigo Everything You Need To Know About TCP — Brad Fitzpatrick. Apresentação histórica em GoCon que vale rever. YouTube + slides.
  7. artigo The Tangled Web of HTTPS — Julia Evans. jvns.ca/blog/2017/07/27/how-does-https-work/ — TLS e certificados explicados com clareza incomum.
  8. docs RFC 9110 — HTTP Semantics. rfc-editor.org/rfc/rfc9110.html — a referência atual, unificando HTTP/1.1, HTTP/2 e HTTP/3 em uma camada de semântica comum.
  9. docs RFC 8446 — TLS 1.3. rfc-editor.org/rfc/rfc8446.html — para TLS moderno. Denso, mas seções 1-2 dão visão geral acessível.
  10. docs Mozilla SSL Configuration Generator. ssl-config.mozilla.org — gera configs TLS recomendadas (Modern, Intermediate, Old) para Nginx, Apache, etc. Use em produção.
  11. vídeo HTTP/3 Deep-Dive — Robin Marx. YouTube. Robin Marx é referência em QUIC/HTTP/3. Palestras dele são técnicas e claras.
  12. paper Congestion Avoidance and Control — Van Jacobson (1988). SIGCOMM 1988. O paper que introduziu slow start e congestion control. Fundacional, leitura curta.