5. Control de flujo en TCP

El control de flujo impide que un emisor rápido desborde la memoria del receptor. TCP logra este equilibrio mediante ventanas deslizantes que crecen o se contraen según la capacidad real de ambos extremos.

A diferencia del control de congestión, que observa el estado de la red, el control de flujo se concentra en el host destino. Su objetivo es sencillo: entregar datos al ritmo que la aplicación receptora puede procesarlos, sin sacrificar la continuidad de la sesión.

5.1 Ventana deslizante en pocas palabras

TCP organiza los bytes en una cola lógica y mantiene tres zonas:

  • Datos enviados y confirmados: ya cuentan con ACK, por lo que el emisor puede liberar memoria.
  • Datos enviados sin confirmación: todavía están “en vuelo”. No se pueden descartar hasta recibir el ACK.
  • Datos autorizados a enviarse: delimitados por la ventana anunciada, representan el próximo tramo listo para salir.

La ventana deslizante se desplaza cuando llegan nuevos ACK. Si el receptor anuncia más espacio, el emisor puede seguir empujando datos; si la ventana se encoge, el emisor debe pausar hasta nuevo aviso.

5.2 Advertised window y ventana efectiva

Cada segmento TCP contiene el campo Window Size, conocido como advertised window. Indica cuántos bytes adicionales puede recibir el host antes de saturarse.

Concepto Descripción
Ventana anunciada Capacidad disponible en el receptor según sus buffers de socket.
Ventana de congestión Limitación introducida por el control de congestión; depende del estado de la red.
Ventana efectiva El mínimo entre ventana anunciada y ventana de congestión; determina los bytes permitidos en vuelo.

Las opciones de escalado de ventana, definidas en RFC 7323, permiten multiplicar el valor del campo cuando se necesitan buffers gigantescos, algo común en enlaces de alta velocidad.

5.3 Ventanas cero y persistencia

Si la aplicación receptora se atasca, el host puede anunciar una ventana cero. El emisor detiene los envíos y activa el temporizador de persistencia. Cada cierto tiempo manda un probe de uno o dos bytes para verificar si la otra parte volvió a tener espacio.

Una ventana cero prolongada suele indicar procesos bloqueados, falta de memoria o filtros intermedios que ralentizan la entrega al socket de usuario.

5.4 Ajuste dinámico del tamaño de ventana

Sistemas modernos incluyen algoritmos de autoajuste que miden el rendimiento de la aplicación y el hardware subyacente. En Windows, el autotuning regula la Receive Window para exprimir enlaces de alta latencia sin intervención manual.

El emisor también adapta su ritmo: cuando llegan ACK rápidos, interpreta que puede ampliar la ventana; si se acumulan ACK retrasados o aparecen duplicados, reduce la cantidad de datos pendientes. Esta retroalimentación protege al receptor y evita retransmisiones costosas.

5.5 Interacción con Nagle y delayed ACK

El algoritmo de Nagle agrupa bytes pequeños para enviar segmentos llenos, mientras que delayed ACK espera un breve lapso para confirmar varios segmentos a la vez. Combinados, pueden introducir latencia adicional en aplicaciones interactivas.

  • Escenarios recomendados: transferencias masivas, protocolos que no dependen de respuestas inmediatas.
  • Escenarios problemáticos: Telnet, SSH o cualquier flujo que envía pocos bytes por vez; suele desactivarse Nagle para reducir la espera.

5.6 Visualización de la ventana en Wireshark

En una captura de Wireshark se pueden habilitar las columnas “Window Size Value” y “Calculated window size”. Esta última ya incorpora el factor de escalado, lo que facilita interpretar cuántos bytes en vuelo permite el receptor.

Los gráficos de flujo disponibles en Statistics > TCP Stream Graphs muestran cómo la ventana crece a medida que la aplicación consume datos y disminuye cuando se congestiona.

5.7 Simulación de ventana deslizante en Python

El siguiente script simplifica el comportamiento de la ventana anunciada y muestra cómo el emisor ajusta su ritmo ante ACK lentos o ventanas cero:

Modelo básico de control de flujo

from collections import deque

def simular_control_flujo(datos, tam_segmento=8, ventana_inicial=24):
    cola = deque([datos[i:i + tam_segmento] for i in range(0, len(datos), tam_segmento)])
    ventana = ventana_inicial
    en_vuelo = []
    secuencia = 0

    while cola or en_vuelo:
        while cola and sum(len(seg) for _, seg in en_vuelo) + len(cola[0]) <= ventana:
            segmento = cola.popleft()
            en_vuelo.append((secuencia, segmento))
            print(f"ENVÍO {secuencia:04d} -> {segmento!r} (ventana {ventana})")
            secuencia += len(segmento)

        if not en_vuelo:
            break

        ack_seq, segmento = en_vuelo.pop(0)
        if len(segmento) < tam_segmento // 2:
            ventana = max(tam_segmento, ventana // 2)
            print(f"ACK lento de {ack_seq:04d}, ventana baja a {ventana}")
        else:
            ventana += len(segmento)
            print(f"ACK {ack_seq:04d} recibido, ventana sube a {ventana}")

texto = "Controlar la ventana evita desbordes del receptor y sostiene la eficiencia."
simular_control_flujo(texto)

Aunque es un modelo sencillo, ayuda a visualizar cómo la ventana efectiva depende de la respuesta del receptor y por qué un ACK lento reduce temporalmente el envío.

5.8 Ajustes y diagnósticos en PowerShell

Windows permite inspeccionar los perfiles de TCP para verificar si el autotuning del buffer de recepción está habilitado:

Cmdlets útiles

Get-NetTCPSetting -SettingName Internet | 
  Select-Object SettingName, AutoTuningLevelLocal

netsh interface tcp show global | Select-String "Receive Window Auto-Tuning Level"

Get-NetTCPSetting expone el modo de autoajuste por perfil, mientras que netsh interface tcp show global entrega el mismo dato en sistemas heredados. Si el valor indica disabled, conviene habilitarlo para aprovechar mejor enlaces de alta latencia siempre que las aplicaciones lo permitan.

5.9 Relación con el control de congestión

Aunque son mecanismos distintos, ambos comparten la idea de ventana. El control de flujo protege al receptor; el de congestión protege a la red. El emisor sólo puede enviar la cantidad de datos que cumpla ambas restricciones simultáneamente. Por eso, monitorear la advertised window es tan importante como ajustar parámetros de congestion avoidance.

5.10 Recomendaciones finales

  • Dimensionar buffers de socket según la carga prevista evita ventanas cero frecuentes.
  • Deshabilitar Nagle únicamente cuando la aplicación lo justifique; de lo contrario se desperdicia ancho de banda.
  • Usar herramientas de captura para confirmar que los ACK avanzan y que la ventana no queda estática por largos periodos.

Con un control de flujo bien configurado, TCP se mantiene estable incluso cuando conviven aplicaciones con ritmos muy diferentes en un mismo enlace.