07 - Controle de Fluxo (Sliding Window)
video: https://youtu.be/... no frontmatter da nota.O Controle de Fluxo (Flow Control) é o mecanismo de regulação que impede que um remetente TCP sobrecarregue o buffer de recepção do destino. A técnica central utilizada é a Janela Deslizante (Sliding Window), em que o receptor anuncia continuamente quanto espaço livre tem disponível. Esse mecanismo é definido na RFC 793 e estendido na RFC 7323 (Window Scaling).
1. Motivação: Por que Controlar o Fluxo?
Mesmo que a rede seja rápida e o remetente tenha muita largura de banda, o computador receptor pode ter uma CPU lenta ou pouca memória RAM disponível para processar os dados recebidos. Se o remetente enviar dados mais rápido do que o receptor consegue ler (recv()/read()) da socket, o buffer de recepção do receptor encherá e começará a descartar novos pacotes — desperdiçando largura de banda com dados que serão retransmitidos.
O controle de fluxo resolve esse problema localmente, entre os dois hosts fim-a-fim, sem envolver a rede (ao contrário do controle de congestionamento, que é uma regulação contra a rede).
2. Conceito de Janela Deslizante
A "janela" é o intervalo de bytes que o remetente pode enviar sem ter recebido ACK. À medida que chegam ACKs, a janela desliza para a direita (frente), liberando novos bytes para envio.
Variáveis principais (definidas na RFC 793 e refinadas em implementações modernas):
| Variável | Significado |
|---|---|
SND.UNA |
Número do primeiro byte não confirmado (left edge da janela) |
SND.NXT |
Número do próximo byte a ser enviado |
SND.WND |
Tamanho da janela de envio (anunciado pelo receptor) |
SND.MAX |
Limite superior: SND.UNA + SND.WND |
rwnd |
Receiver Window — espaço livre no buffer do receptor |
cwnd |
Congestion Window — limite imposto pelo controle de congestionamento |
effective window |
min(rwnd, cwnd) — o que realmente limita o envio |
A janela efetiva que o remetente pode explorar é min(rwnd, cwnd): ele respeita tanto o receptor quanto a rede.
Diagrama da Janela Deslizante
Bytes: 1000 4000 7000
|-------- enviados -------|---- na janela ------|----- proibido ----|
^ ^ ^
SND.UNA SND.NXT SND.UNA + SND.WND
(próximo ACK esperado) (próximo a transmitir) (limite da janela)
Quando o ACK 4000 chega, SND.UNA pula para 4000 e toda a janela desliza para a direita, abrindo novos bytes para transmissão.
3. Anúncio da Janela pelo Receptor
O campo Window Size do cabeçalho TCP tem 16 bits. Portanto, sem opções, o receptor só pode anunciar até 65535 bytes (64 KiB) de espaço livre. Em cada segmento que envia (incluindo ACKs puros), o receptor escreve nesse campo o tamanho atual do seu buffer de recepção livre.
TCP Header:
...
Sequence Number: 32 bits
Acknowledgment Number: 32 bits
Data Offset + Reserved + Flags
Window Size: 16 bits <-- rwnd anunciado
Checksum: 16 bits
Urgent Pointer: 16 bits
Options (variable)
4. Processo de Regulação
- Anúncio de Capacidade: O receptor diz ao remetente: "Eu tenho espaço para mais
Xbytes" (campo Window = X). - Envio Limitado: O remetente mantém a variável
cwnd(congestion window) e respeita a regra:Tamanho do Envio = min(rwnd, cwnd). O remetente nunca envia mais dados do que o receptor disse que pode aguentar. - Esvaziamento do Buffer: Quando a aplicação no receptor lê dados do buffer (
recv()), espaço é liberado. - Atualização: O receptor envia novos ACKs com o valor atualizado do
rwnd, aumentando a janela e permitindo que o remetente acelere novamente.
5. Window Scaling (RFC 7323)
O limite de 64 KiB é severo para enlaces modernos de alta latência (ex: 100 ms RTT, 1 Gbps): em 64 KiB, a taxa máxima seria 5,2 Mbps — ridícula. A solução é o Window Scale Option, negociada no handshake:
- Durante o SYN, o cliente envia a opção
Window Scale: <shift>(1 byte, valor 0-14). - Se o servidor também enviar a opção no SYN-ACK, ela está ativa para a conexão.
- A partir daí, todo valor do campo Window Size é multiplicado por
2^shift. - Com shift 14, o limite sobe para 1 GB (65535 × 214 = 1.073.725.440 bytes).
# Verificar se window scaling está ativo (Linux):
sysctl net.ipv4.tcp_window_scaling # 1 = habilitado (default)
# Em uma captura:
tcpdump -n -i any 'tcp[tcpflags] == tcp-syn' -v
# ... options [mss 1460,nop,wscale 9,nop,nop,sackOK,TS val-ecri ...
6. O Problema da Janela Zero (Zero Window)
Se a aplicação no receptor estiver muito lenta e o buffer encher completamente, o receptor enviará um rwnd = 0.
- Bloqueio: O remetente deve parar de enviar novos dados de usuário imediatamente.
- Manutenção da Conexão: O remetente entra em modo de persistência. Ele envia pequenos segmentos de teste chamados Zero Window Probes (sondas de janela zero) para verificar se o receptor já liberou algum espaço.
- A primeira sonda é enviada após o tempo máximo de persistência (max persist timer, ~60 s).
- Em Linux: probes a cada
tcp_retries2(=15) retransmissões, depois aborta. - Desbloqueio: Assim que o receptor tiver espaço, ele responde à sonda com um novo
rwnd > 0, e o remetente retoma a transmissão normal.
Pegadinha: atualização de janela perdida
Imagine que o receptor libera 1000 bytes e envia um ACK com Window = 1000. Se esse pacote for perdido na rede, o remetente nunca saberá — e ficará parado aguardando indefinidamente. Para evitar esse deadlock, o remetente envia as Zero Window Probes; o receptor, ao respondê-las, reanuncia o rwnd atual.
7. Silly Window Syndrome (SWS)
Outra patologia clássica (descrita na RFC 1122): se o receptor libera 1 byte por vez, ele anuncia Window = 1 repetidamente, e o remetente envia segmentos de 1 byte — desperdiçando 40+ bytes de cabeçalho por byte útil.
A solução é dupla:
- Clark's Solution (receptor): Não anunciar janelas pequenas. Só enviar
Window > 0quando houver espaço suficiente para um MSS ou para metade do buffer (o que for maior). - Nagle's Algorithm (remetente): Não enviar segmentos pequenos se houver dados não confirmados em trânsito — acumular até completar um MSS ou receber ACK.
No Linux:
sysctl net.ipv4.tcp_sack # SACK
sysctl net.ipv4.tcp_nagle # 1 = habilita Nagle (default)
8. Diferença Crucial: Fluxo x Congestionamento
| Aspecto | Controle de Fluxo | Controle de Congestionamento |
|---|---|---|
| Defende quem? | O receptor (buffer) | A rede (roteadores) |
| Variável | rwnd (16 bits no header) |
cwnd (interno, algoritmo) |
| Quem decide | Receptor (anuncia) | Remetente (infere por perdas) |
| Sinal explícito | Sim, no header | Não, inferido |
| Reage a | Lentidão do receptor | Perdas, duplicação de ACKs, ECN |
O envio real obedece sempre min(rwnd, cwnd). Se a rede está saudável mas o receptor lento, o gargalo é rwnd; se o receptor está ocioso mas a rede congestionada, o gargalo é cwnd.
9. Exemplo de Evolução da Janela
Buffer receptor de 64 KiB, remetente envia 16 KiB por segmento (janela inicial 65535):
T=0 SND.UNA=1 SND.NXT=16384 Window=65535 (em trânsito: 16383 bytes)
T=10 ACK 16384 chega Window=65535 (desliza; libera +16384)
T=10 SND.NXT=32768 (envia mais 16384)
T=20 aplicação receptora para de ler (recv travado)
T=20 ACK 49152 chega Window=8192 (só sobrou 8 KiB livre)
T=30 remetente reduz envio a 8192
T=40 aplicação receptora retoma leitura -> próximo ACK: Window=65535 (full)
10. Resumo
A Janela Deslizante é o coração do controle de fluxo TCP. Permite pipelining (envio de múltiplos segmentos sem esperar ACK individual), adapta-se dinamicamente ao consumo do receptor e, combinada com Window Scaling, suporta enlaces de alta banda-delay product. Sintonizar rwnd é tão importante quanto cwnd para o throughput real — em redes rápidas de longa distância, é o rwnd que costuma ditar o limite.
Em Linux, o tamanho dos buffers TCP é ajustável: sysctl net.ipv4.tcp_rmem (mínimo/default/máximo do buffer de recepção) e net.ipv4.tcp_wmem (envio). Em servidores de alto throughput, valores como 4096 87380 6291456 (default moderno) garantem janelas grandes o suficiente para enlaces de múltiplos Gbps.