>_ josetilabs.com
← blog

10 nov 2026 · redes, linux, tcp, udp · 4 min

TCP e UDP na prática: quando a diferença aparece

Parágrafo-resposta: TCP e UDP diferem em uma coisa só: TCP reenvia o que se perdeu, UDP não. Com 5% de perda simulada em um link de 5 MB, o TCP levou 5,6 segundos em vez de 2,0 e fez 5 a 7 retransmissões; o UDP perdeu 4,5% dos pacotes e nem percebeu. Escolha TCP quando o dado precisa chegar inteiro e UDP quando atraso custa mais que perda.

O que cada um faz

TCP abre uma conexão, numera cada byte e confirma o recebimento. Se um pacote se perde, o remetente descobre pela falta de confirmação e reenvia. UDP manda datagramas e segue em frente; não há confirmação, reenvio nem ordem garantida.

A diferença aparece quando o link não é perfeito. Em link limpo, os dois entregam o mesmo arquivo no mesmo tempo. Com perda, o TCP segura o fluxo até retransmitir, e o UDP segue sem o que faltou.

A medição abaixo usa 5% de perda, valor alto para uma rede local, mas comum em enlace sem fio distante.

Medição

Um arquivo de 5 MB (5 242 880 bytes) foi enviado por TCP e por UDP sobre o loopback de um contêiner, com e sem perda simulada por tc netem. Sem perda, TCP levou 2,0 s e UDP 2,0 s. Com 5% de perda e 50 ms de atraso, TCP levou 5,6 s com 5 a 7 retransmissões; UDP perdeu 45 de 1000 pacotes (4,5%) e terminou em 2,0 s.

<|start|> cenário | TCP tempo | TCP retransmissões | UDP pacotes recebidos | |—|—|—|—| | sem perda | 2,0 s | 0 | 1000 de 1000 | | 5% de perda, 50 ms | 5,6 s | 5 a 7 | 955 de 1000 |

A retransmissão do TCP é o preço da entrega completa. O UDP não paga esse preço, mas também não entrega o que faltou.

Terminal com duas medições de TCP, sem perda e com 5% de perda
TCP sem perda: 2,0 s e zero retransmissões. Com 5% de perda: 5,6 s e 5 a 7 retransmissões.

O artefato

O script abaixo monta um laboratório isolado em um contêiner, com netns e tc netem, e roda as duas medições. Requer root e os pacotes iproute2 e netcat-openbsd.

bash
#!/usr/bin/env bash
# medir-tcp-udp.sh
set -euo pipefail

if ! command -v tc >/dev/null 2>&1 || ! command -v nc >/dev/null 2>&1; then
  echo "dependências ausentes: instale iproute2 e netcat-openbsd"
  echo "FIM"
  exit 0
fi

PAYLOAD=/tmp/payload.bin
head -c 5242880 /dev/urandom > "$PAYLOAD"

get_retrans() { grep '^Tcp:' /proc/net/snmp | tail -1 | awk '{print $13}'; }

medir_tcp() {
  local netem="$1"
  tc qdisc del dev lo root 2>/dev/null || true
  if [ "$netem" = "sim" ]; then
    tc qdisc add dev lo root netem loss 5% delay 50ms
  fi
  rm -f /tmp/tcp_recv.bin
  nc -l -p 9000 > /tmp/tcp_recv.bin &
  local rpid=$!
  sleep 0.5
  local r0=$(get_retrans)
  local start=$(date +%s%N)
  nc -q 2 127.0.0.1 9000 < "$PAYLOAD"
  local end=$(date +%s%N)
  wait "$rpid" 2>/dev/null || true
  local r1=$(get_retrans)
  local recv=$(stat -c %s /tmp/tcp_recv.bin 2>/dev/null || echo 0)
  printf 'TCP %s: enviados=%s recebidos=%s tempo_ms=%s retransmissoes=%s\n' \
    "$netem" "$(stat -c %s "$PAYLOAD")" "$recv" "$(( (end-start)/1000000 ))" "$((r1-r0))"
}

medir_udp() {
  local netem="$1"
  tc qdisc del dev lo root 2>/dev/null || true
  if [ "$netem" = "sim" ]; then
    tc qdisc add dev lo root netem loss 5% delay 50ms
  fi
  python3 - "$netem" <<'PY'
import socket, time, sys
netem = sys.argv[1]
n = 1000
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
count = 0
recv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
recv.bind(("127.0.0.1", 9002))
recv.settimeout(3)
import threading
def listener():
    global count
    deadline = time.time() + 6
    while time.time() < deadline:
        try:
            recv.recvfrom(2048)
            count += 1
        except socket.timeout:
            break
t = threading.Thread(target=listener, daemon=True)
t.start()
time.sleep(1)
for _ in range(n):
    sock.sendto(b"x" * 100, ("127.0.0.1", 9002))
    time.sleep(0.002)
t.join()
print(f"UDP {netem}: enviados={n} recebidos={count}")
PY
}

medir_tcp "nao"
medir_tcp "sim"
medir_udp "nao"
medir_udp "sim"
tc qdisc del dev lo root 2>/dev/null || true
echo "FIM"

Como verificar

Rode o script em um contêiner com --cap-add=NET_ADMIN. A saída esperada tem quatro linhas, a última FIM:

TCP nao: enviados=5242880 recebidos=5242880 tempo_ms=2009 retransmissoes=0
TCP sim: enviados=5242880 recebidos=5242880 tempo_ms=5627 retransmissoes=5
UDP nao: enviados=1000 recebidos=1000
UDP sim: enviados=1000 recebidos=955
FIM

Se os números de retransmissão variarem entre execuções, isso é normal: o netem usa semente aleatória.

Se der errado

tc: Operation not permitted: o contêiner precisa de --cap-add=NET_ADMIN. Sem essa capacidade, o netem não aplica e a medição vira um teste sem perda.

nc: command not found: instale netcat-openbsd. O nc do pacote netcat-traditional tem sintaxe diferente para -q.

python3 sem módulo socket: improvável; socket é da biblioteca padrão. Se o interpretador for um build mínimo, use o python3 da distribuição.

Quando usar qual

Use TCP para arquivo, API, banco, SSH e qualquer dado que precisa chegar inteiro. Use UDP para voz, vídeo ao vivo e jogos, onde um pacote atrasado vale menos que um pacote a tempo. DNS usa UDP por padrão e cai para TCP quando a resposta não cabe em um datagrama.

<|constrain|> protocolo | entrega | ordem | retransmissão | caso típico | |—|—|—|—|—| | TCP | garantida | garantida | sim | arquivo, API, SSH | | UDP | melhor esforço | não garantida | não | voz, vídeo, jogo |

FAQ

TCP é sempre mais lento que UDP? Não. Em link limpo os dois entregam no mesmo tempo. A diferença aparece com perda ou congestionamento.

UDP perde pacote e não avisa? O protocolo não avisa. A aplicação pode implementar confirmação por cima, como o QUIC faz sobre UDP.

Dá para simular perda sem root? Não com tc. Existem ferramentas em espaço de usuário, mas fogem do escopo deste texto.

Por que o TCP retransmitiu só 5 vezes para 5% de perda? Porque a perda foi de 5% dos pacotes, e o algoritmo de retransmissão agrupa bytes em segmentos. O número exato varia a cada execução.

Vale a pena reproduzir a medição? Sim, se você for escolher protocolo para um serviço próprio. A medição leva menos de um minuto em um contêiner descartável.

Fontes

Veja também o que é um contêiner de verdade e processos, portas e conexões.

Leia também

// Por e-mail

Os textos publicados aqui também vão por e-mail. O envio ainda não começou: quem deixar o endereço agora entra na primeira lista.

Guardo só o seu e-mail, a data e o registro desta autorização. Sem cookies, sem rastreador e nada compartilhado com terceiros. Detalhes na política de privacidade.