TCP 흐름 제어 — 슬라이딩 윈도우, 혼잡 제어 알고리즘
TCP가 데이터를 안정적으로 전송하면서도 네트워크를 과부하시키지 않는 비결이 흐름 제어와 혼잡 제어입니다. 이 두 가지는 다른 목적의 메커니즘입니다.
흐름 제어 vs 혼잡 제어
| 흐름 제어 (Flow Control) | 혼잡 제어 (Congestion Control) | |
|---|---|---|
| 목적 | 수신자 버퍼 오버플로 방지 | 네트워크 과부하 방지 |
| 대상 | 송신자 ↔ 수신자 | 송신자 ↔ 네트워크 |
| 제어 변수 | rwnd (수신 윈도우) | cwnd (혼잡 윈도우) |
실제 전송 윈도우 = min(rwnd, cwnd)
슬라이딩 윈도우 (Sliding Window)
전송 가능 범위 (윈도우):
┌────────────────────────────────────────────┐
│ 전송완료+ACK │ 전송했지만 │ 전송 가능 │ 아직 안 보냄 │
│ (확인됨) │ ACK 미수신 │ (윈도우 내) │ (윈도우 밖) │
└────────────────────────────────────────────┘
↑ ↑
윈도우 시작 윈도우 끝
ACK가 오면 → 윈도우가 오른쪽으로 슬라이드
수신자는 TCP 헤더의 Window Size 필드로 자신의 남은 버퍼 크기를 알립니다.
수신자: "내 버퍼에 8KB 남았어" → Window Size = 8192
송신자: "그럼 8KB까지만 보내고 ACK 기다릴게"
Zero Window
수신자 버퍼가 가득 차면 Window Size = 0을 알립니다.
수신자: Window = 0 → "더 보내지 마"
송신자: Window Probe를 주기적으로 전송 → "아직도 0이야?"
수신자: Window = 4096 → "이제 보내도 돼"
혼잡 제어 알고리즘
Slow Start (느린 시작)
연결 시작 시 cwnd를 1 MSS부터 지수적으로 증가 시킵니다.
RTT 1: cwnd = 1 MSS → 1개 전송
RTT 2: cwnd = 2 MSS → 2개 전송
RTT 3: cwnd = 4 MSS → 4개 전송
RTT 4: cwnd = 8 MSS → 8개 전송
...
ssthresh(임계값)에 도달하면 → Congestion Avoidance로 전환
Congestion Avoidance (혼잡 회피)
ssthresh 이후 cwnd를 선형적으로 증가 (RTT당 1 MSS씩).
cwnd = 16 → 17 → 18 → 19 → ... (AIMD: 매 RTT마다 +1)
패킷 손실 감지 시
타임아웃 발생:
→ ssthresh = cwnd / 2
→ cwnd = 1 MSS (Slow Start부터 재시작)
3 Duplicate ACK (Fast Retransmit):
→ ssthresh = cwnd / 2
→ cwnd = ssthresh + 3 (Fast Recovery)
CUBIC (리눅스 기본)
리눅스의 기본 혼잡 제어 알고리즘입니다.
- 3차 함수(cubic function)로 cwnd를 조절
- 손실 전 cwnd 크기를 기억하여 빠르게 복구
- 고대역폭-고지연 네트워크에서 AIMD보다 효율적
# 현재 사용 중인 혼잡 제어 알고리즘 확인
cat /proc/sys/net/ipv4/tcp_congestion_control
# cubic
# 사용 가능한 알고리즘
cat /proc/sys/net/ipv4/tcp_available_congestion_control
# reno cubic bbr
핵심 포인트
- 흐름 제어 vs 혼잡 제어: 흐름 제어는 수신자 보호, 혼잡 제어는 네트워크 보호
- Slow Start가 "느린" 이유가 아님: 이전에는 전체 윈도우를 한 번에 보냈는데, Slow Start는 점진적으로 증가
- 3 Duplicate ACK vs Timeout: 3 Dup ACK은 네트워크가 아직 동작 중이므로 cwnd를 덜 줄임
정리
TCP의 흐름 제어(슬라이딩 윈도우)는 수신자의 처리 속도에 맞추고, 혼잡 제어(Slow Start + AIMD/CUBIC)는 네트워크 상태에 맞춰 전송 속도를 조절합니다. 이 두 메커니즘의 역할과 차이를 구분하는 것이 핵심입니다.