Confiabilidade, controle de fluxo e congestionamento

Entregar bytes exige mais do que reenviar o que sumiu
Na aula sobre TCP, UDP, portas e sockets, vimos que TCP apresenta à aplicação um fluxo confiável e ordenado de bytes. Essa frase resume o serviço, mas não revela o trabalho necessário para construí-lo sobre IP: pacotes podem se perder, atrasar, duplicar ou chegar fora de ordem.
As duas pontas precisam responder a três problemas diferentes:
- confiabilidade: descobrir quais bytes chegaram e recuperar lacunas;
- controle de fluxo: não enviar mais do que o receptor consegue armazenar e entregar à aplicação;
- controle de congestionamento: não pressionar o caminho compartilhado além do que ele aparenta suportar.
Esses mecanismos cooperam, mas não são sinônimos. Um servidor com pouca memória disponível pode limitar o fluxo mesmo em uma rede vazia. Uma rede congestionada pode limitar a transmissão mesmo quando o servidor possui espaço de sobra.
Esta aula usa TCP como principal exemplo porque esses mecanismos fazem parte de sua especificação e de suas extensões. Outros transportes também podem oferecer confiabilidade e controlar congestionamento, inclusive protocolos construídos sobre UDP, mas com formatos e algoritmos próprios.
Números de sequência organizam o fluxo de bytes
TCP numera os bytes, não apenas os segmentos. Se um segmento carrega 1.000 bytes a partir do número de sequência 1, ele cobre conceitualmente o intervalo 1–1000. O próximo pode começar em 1001.
O receptor usa o campo de reconhecimento, ou ACK, para informar o próximo número de sequência que espera. Um ACK 1001 significa: “recebi, em sequência, tudo antes de 1001”. Esse é um reconhecimento cumulativo.
Considere quatro intervalos enviados:
1–1000 | 1001–2000 | 2001–3000 | 3001–4000
Se o segundo intervalo se perder, mas os dois seguintes chegarem, o receptor ainda não pode avançar o ACK cumulativo além de 1001. Há uma lacuna no fluxo. Ele pode guardar temporariamente os bytes posteriores, conforme sua implementação e recursos, enquanto continua indicando o ponto ausente.
Quando 1001–2000 chega por retransmissão, a sequência contínua passa a alcançar 4000. O ACK então pode avançar diretamente para 4001.
Números de sequência também ajudam a reconhecer duplicatas. Uma retransmissão pode chegar depois do segmento original apenas atrasado; como o receptor conhece os intervalos já obtidos, não precisa entregar os mesmos bytes duas vezes à aplicação.
ACK cumulativo e SACK descrevem a lacuna de modos diferentes
O ACK cumulativo indica somente a borda esquerda ainda ausente. A opção Selective Acknowledgment, ou SACK, acrescenta informação sobre blocos não contíguos que já chegaram.
No exemplo anterior, a mensagem pode manter ACK 1001 e, ao mesmo tempo, informar por SACK que o bloco contíguo 2001–4001 está armazenado. Assim, a origem percebe que o buraco está em 1001–2000 e evita retransmitir dados que o destino já possui.
SACK não muda o significado do ACK cumulativo. Os blocos seletivos são informação complementar e são negociados como capacidade durante a abertura da conexão.
Nem todo ACK repetido prova sozinho que houve perda. Reordenação no caminho também pode fazer segmentos posteriores chegarem antes. Algoritmos de detecção combinam evidências, tempo e estado da conexão para decidir quando retransmitir.
Retransmissão depende de detectar a perda
Reenviar imediatamente qualquer segmento ainda não confirmado criaria duplicatas desnecessárias. Esperar demais deixaria a aplicação parada. A origem precisa inferir que um intervalo provavelmente não chegará em tempo útil.
Há diferentes caminhos para essa inferência:
- temporizador de retransmissão: dados permanecem sem confirmação além do RTO;
- feedback de ACKs: reconhecimentos mostram que dados posteriores avançaram enquanto uma lacuna permaneceu;
- SACK: blocos recebidos ajudam a localizar o que falta;
- métodos baseados em tempo recente: mecanismos como RACK usam a entrega temporal de dados posteriores para inferir perdas;
- sondas de perda na cauda: TLP busca obter feedback quando poucos segmentos no final do envio desapareceram.
O fast retransmit clássico usa uma sequência de ACKs duplicados como evidência de que um segmento saiu do caminho enquanto outros continuaram chegando. Ele pode iniciar recuperação antes do temporizador expirar. Extensões modernas melhoram situações com várias perdas, reordenação ou pouco dado em voo.
RTT e sua variação orientam o temporizador
O round-trip time, ou RTT, é o tempo entre enviar algo que produz feedback e receber esse retorno. Ele inclui propagação, transmissão, processamento e espera em filas nas duas direções.
Um temporizador fixo funciona mal em uma Internet heterogênea:
- se for menor que o RTT normal, dispara retransmissões de dados apenas atrasados;
- se for grande demais, demora a recuperar uma perda real;
- se ignorar variações, reage mal quando as filas e os caminhos mudam.
Por isso, o RTO é calculado a partir de uma estimativa suavizada do RTT e de sua variação. A RFC 6298 estabelece um cálculo conservador e regras para reiniciar o temporizador e aumentar a espera após expirações consecutivas. O objetivo não é “adivinhar a velocidade da Internet”, mas escolher um limite adaptativo com margem para a incerteza observada.
Uma amostra de RTT também precisa ser interpretada com cuidado: após retransmitir, pode ser ambíguo saber se um ACK correspondeu à primeira transmissão ou à cópia. Timestamps e regras de amostragem ajudam implementações a obter medições úteis.
Controle de fluxo protege o receptor
Os bytes recebidos ficam em uma área mantida pela pilha antes de a aplicação consumi-los. Se a aplicação lê lentamente ou o sistema está sob pressão, esse espaço pode diminuir.
O receptor anuncia no cabeçalho TCP quanto espaço adicional está disposto a aceitar. Essa medida é a receive window, normalmente abreviada como rwnd. À medida que a aplicação lê dados e libera o buffer, o receptor pode anunciar uma janela maior.
O remetente deve respeitar essa borda. Se o destino anuncia espaço para 48 KiB, a origem não deve manter um volume arbitrário de novos bytes além do que a janela permite, mesmo que o enlace tenha capacidade muito maior.
O campo básico de janela no cabeçalho possui 16 bits. A opção Window Scale, negociada durante o handshake, permite representar janelas maiores por um fator de escala. Isso é importante em caminhos cujo produto entre capacidade e atraso excede a janela básica.
Se a janela anunciada chega a zero, o receptor está dizendo que, naquele momento, não aceita novos dados do fluxo. A origem entra na condição persistente e realiza sondagens controladas. Isso evita que a conexão fique parada para sempre caso a atualização que reabriria a janela seja perdida.
Controle de congestionamento protege o caminho compartilhado
O remetente não conhece diretamente todas as filas, enlaces e fluxos concorrentes entre as pontas. Ele mantém uma estimativa operacional de quanto dado pode deixar em voo sem pressionar demais o caminho: a congestion window, ou cwnd.
Ao contrário de rwnd, cwnd não é um valor anunciado pelo receptor no campo Window. Ela é estado mantido pela origem segundo o algoritmo de congestionamento e o feedback observado.
Sinais podem incluir:
- perda inferida por ACKs ou tempo;
- marcação explícita de congestionamento, quando ECN é suportado no caminho e nas pontas;
- evolução dos reconhecimentos e do volume entregue;
- aumento do atraso, em algoritmos que também usam esse tipo de evidência.
Algoritmos diferentes interpretam e respondem a sinais de maneiras diferentes. O princípio compartilhado é adaptar a quantidade enviada e evitar crescimento ilimitado em uma rede de capacidade finita e compartilhada.
A origem respeita as duas janelas
Em um modelo didático, o volume de dados não confirmados fica limitado pelo menor valor entre a janela de recepção e a janela de congestionamento:
dados em voo ≤ min(rwnd, cwnd)
Se rwnd = 48 KiB e cwnd = 20 KiB, a rede é o limite mais restritivo naquele instante: até 20 KiB podem permanecer em voo nesse exemplo. Se rwnd cair para 8 KiB, o receptor passa a ser o limite.
Essa expressão não descreve todo o estado de uma implementação. A quantidade disponível na aplicação, bytes já enviados, tamanho dos segmentos, pacing, recuperação de perdas e regras do sistema também afetam o próximo envio. Ainda assim, ela separa corretamente as duas pressões.
Isso também melhora o diagnóstico. Se a captura mostra janela anunciada próxima de zero, investigue consumo no receptor. Se rwnd permanece ampla, mas a origem reduz o envio após perdas ou marcações, observe o caminho e o controle de congestionamento.
Slow start não significa começar devagar em valor absoluto
Quando uma conexão começa, a origem ainda possui pouca evidência sobre a capacidade disponível. O mecanismo chamado slow start aumenta a janela de congestionamento conforme ACKs retornam. Em termos conceituais, o crescimento pode se aproximar de uma duplicação por RTT enquanto há dados suficientes e nenhum limite menor interfere.
O nome compara esse comportamento com enviar uma janela enorme de uma só vez. Em redes atuais, a janela inicial pode transportar vários segmentos; portanto, “slow” não deve ser traduzido como transmissão necessariamente lenta para toda aplicação.
O crescimento inicial termina ou muda de fase quando ocorre um sinal de congestionamento, quando a janela alcança um limiar ou quando outro mecanismo decide que é prudente avançar de forma mais cautelosa.
Congestion avoidance explora capacidade com mais cautela
No modelo Reno da RFC 5681, a fase de congestion avoidance aumenta cwnd de maneira aproximadamente linear, enquanto um evento de congestionamento provoca redução multiplicativa. Essa combinação é frequentemente resumida como AIMD: aumento aditivo e diminuição multiplicativa.
O gráfico de Reno é útil para aprender o ciclo, mas não representa toda conexão atual. CUBIC, padronizado na RFC 9438 e amplamente implantado, calcula o crescimento com uma função cúbica baseada no tempo desde o último evento. Ele busca melhor utilização em caminhos de alta capacidade e alto atraso sem abandonar princípios de resposta ao congestionamento.
Reno e CUBIC podem desenhar curvas diferentes. Ambos mantêm uma janela, recebem feedback e reduzem pressão diante de eventos definidos por seus algoritmos. QUIC também possui controle de congestionamento, embora rode sobre UDP e não reutilize literalmente todo o estado TCP.
Perda é um sinal útil, não uma explicação completa
Algoritmos baseados em perda tratam descartes como indício de que o caminho atingiu um limite. Isso foi fundamental para estabilizar o compartilhamento da Internet, mas a causa física de uma perda pode ser outra:
- corrupção em um meio sem fio;
- mudança de rota e reordenação;
- falha transitória de enlace ou equipamento;
- política de fila ou de policiamento;
- buffer do host ou da interface esgotado;
- congestionamento real em um gargalo.
TCP não precisa conhecer a causa física para recuperar bytes. Entretanto, o algoritmo pode reduzir cwnd porque, do ponto de vista fim a fim, reagir conservadoramente é mais seguro do que presumir capacidade livre.
ECN permite que equipamentos marquem pacotes em vez de depender somente do descarte, quando todos os participantes necessários oferecem suporte. A marca chega à origem por feedback e pode provocar redução antes de uma fila transbordar. ECN complementa o controle; não cria capacidade nem elimina filas.
Filas transformam excesso em atraso antes do descarte
Um enlace transmite uma quantidade finita de bits por segundo. Quando pacotes chegam mais rápido do que podem sair, eles esperam em uma fila. Uma fila curta absorve rajadas; uma fila persistentemente cheia acrescenta latência e pode terminar em descarte.
Esse atraso explica por que um teste pode exibir boa vazão e, ao mesmo tempo, piorar chamadas, jogos ou navegação. O gargalo está ocupado e os pacotes interativos aguardam atrás de uma fila extensa.
Três medidas não devem ser confundidas:
- capacidade: limite nominal ou disponível de transporte no caminho;
- throughput: volume entregue por unidade de tempo;
- latência: tempo gasto pela comunicação, incluindo filas e processamento.
O produto largura de banda × atraso ajuda a estimar quanto dado precisa permanecer em voo para ocupar o caminho. Janelas pequenas demais podem deixar capacidade ociosa em caminhos longos; janelas grandes sem controle adequado podem ampliar filas. É justamente por isso que escala de janela e controle de congestionamento resolvem problemas diferentes.
Entrega em ordem pode fazer bytes posteriores esperarem
TCP entrega o fluxo em ordem à aplicação. Se um intervalo falta e bytes posteriores já chegaram, esses bytes podem aguardar a recuperação da lacuna antes de serem apresentados. Esse efeito é conhecido como bloqueio de cabeça de fila dentro do fluxo.
SACK melhora a informação para retransmitir apenas o necessário, mas não muda a promessa de ordem do fluxo TCP. Abrir várias conexões ou usar um transporte com múltiplos fluxos independentes altera a arquitetura, não “desliga” a confiabilidade de uma conexão existente.
Esse detalhe conecta desempenho e semântica: recuperar rapidamente uma lacuna reduz o tempo em que dados úteis ficam presos atrás dela.
Diagnostique receptor, caminho e origem separadamente
Quando uma transferência oscila ou fica lenta, reúna evidências antes de atribuir a causa:
- confirme RTT básico e sua variação durante o problema;
- observe se existem retransmissões, SACKs, ACKs duplicados ou marcações ECN;
- compare a janela anunciada pelo receptor ao longo do tempo;
- verifique se a aplicação receptora está lendo os dados e se o host está sob pressão;
- identifique o gargalo e procure crescimento de fila ou descarte nas interfaces;
- compare caminho de ida e volta, pois ACKs também podem atrasar ou se perder;
- diferencie perda real de captura incompleta, offload da placa e reordenação;
- observe se a origem possui dados para enviar e se CPU, disco ou aplicação a limitam;
- repita uma medição controlada e compare horários, destinos e tamanhos;
- relacione o evento ao protocolo de aplicação, não apenas à camada TCP.
Um analisador pode calcular “bytes em voo”, RTT e retransmissões, mas esses valores dependem do ponto de captura. Capturar somente uma direção, depois de NAT, em uma interface com offload ou no meio de uma conexão muda o que pode ser inferido.
O encaminhamento por roteadores ajuda a localizar o caminho. O encapsulamento em camadas ajuda a distinguir o segmento observado do pacote e do quadro que o transportam.
Erros comuns sobre confiabilidade e congestionamento
- “ACK significa que a aplicação processou”: ele confirma estado do fluxo TCP, não a ação de negócio;
- “ACK reconhece o segmento pelo número”: o número cumulativo indica o próximo byte esperado;
- “SACK substitui o ACK normal”: SACK complementa a confirmação cumulativa com blocos não contíguos;
- “toda perda prova congestionamento”: perda é um sinal operacional, mas pode ter outras causas físicas;
- “controle de fluxo e congestionamento são iguais”: um protege o receptor; o outro protege o caminho;
- “rwnd é a velocidade do link”: ela expressa espaço aceito pelo destino;
- “cwnd vem no cabeçalho”: ela é estado local do remetente;
- “slow start sempre é lento”: o nome descreve exploração gradual, não uma taxa absoluta universal;
- “TCP sempre duplica a janela a cada RTT”: outros limites, ACKs, algoritmos e fases alteram o crescimento;
- “janela zero encerra a conexão”: TCP possui condição persistente e sondagens para descobrir a reabertura;
- “retransmitir mais cedo é sempre melhor”: uma decisão precipitada desperdiça capacidade e pode ampliar congestionamento;
- “mais buffer sempre melhora”: filas excessivas podem elevar muito a latência.
O que você deve guardar
TCP usa números de sequência para posicionar bytes, ACK cumulativo para indicar a primeira lacuna e retransmissões para recuperar dados. SACK acrescenta uma visão dos blocos que já chegaram e torna a recuperação de múltiplas perdas mais precisa.
O temporizador acompanha RTT e variação porque o caminho muda. Fast retransmit, SACK e mecanismos como RACK‑TLP podem detectar certas perdas antes de um RTO, mas nenhuma técnica transforma toda ausência de ACK em certeza instantânea.
rwnd nasce da capacidade anunciada pelo receptor. cwnd nasce da estimativa da origem sobre o caminho. O remetente respeita o limite mais restritivo, além das condições da própria aplicação e implementação.
Slow start e congestion avoidance exploram capacidade de forma adaptativa. Perdas, marcações e outros sinais levam o algoritmo a reduzir pressão. A curva exata pode ser Reno, CUBIC ou outra implementação compatível; o princípio é aprender com o caminho e compartilhar recursos finitos.
Referências
- IETF — RFC 9293: Transmission Control Protocol. Acesso em 29 ago. 2026.
- IETF — RFC 6298: Computing TCP's Retransmission Timer. Acesso em 29 ago. 2026.
- IETF — RFC 2018: TCP Selective Acknowledgment Options. Acesso em 29 ago. 2026.
- IETF — RFC 5681: TCP Congestion Control. Acesso em 29 ago. 2026.
- IETF — RFC 7323: TCP Extensions for High Performance. Acesso em 29 ago. 2026.
- IETF — RFC 8985: The RACK-TLP Loss Detection Algorithm for TCP. Acesso em 29 ago. 2026.
- IETF — RFC 9438: CUBIC for Fast and Long-Distance Networks. Acesso em 29 ago. 2026.
- IETF — RFC 6429: TCP Sender Clarification for Persist Condition. Acesso em 29 ago. 2026.
