받는 쪽의 버퍼가 넘치지 않게 보내는 속도를 조절하는 것. 상대를 배려하는 제어다.
어떻게 하나 — 윈도우 광고
수신자가 매 ACK 에 "내 버퍼에 아직 N 바이트 여유 있다" 를 실어 보낸다 송신자는 그 N 을 넘겨서 보내지 않는다
수신 버퍼가 가득 차면 → 윈도우 0 을 광고 → 송신자가 멈춘다 버퍼가 비면 → 다시 광고 (Window Update)
슬라이딩 윈도우
[ 보냈고 ACK 받음 ][ 보냈지만 ACK 대기 ][ 아직 안 보냄 ][ 보낼 수 없음 ]
└──────── 윈도우 ────────┘
ACK 가 오면 윈도우가 오른쪽으로 미끄러진다
→ 한 번에 하나씩 보내고 기다리는 것보다 훨씬 빠르다 (파이프라이닝)
"하나 보내고 ACK 기다리기"였다면 왕복 지연(RTT)마다 한 조각씩만 갈 것이다. 윈도우가 그 제약을 없앤다.
처리량의 상한
최대 처리량 ≈ 윈도우 크기 / RTT
예) 윈도우 64KB · RTT 100ms
- 64KB / 0.1s = 640KB/s ≈ 5Mbps
- 회선이 1Gbps 여도 이보다 못 낸다
장거리·고대역 회선에서 속도가 안 나오는 대표 원인이다. 그래서 윈도우 스케일 옵션으로 최대 1GB까지 늘릴 수 있게 확장됐다.
sysctl net.ipv4.tcp_window_scaling # 1 이어야 한다
sysctl net.ipv4.tcp_rmem # 수신 버퍼 최소/기본/최대
혼잡 제어와 헷갈리지 않기
-
흐름 제어 — 상대(수신자)를 배려한다 — 수신 버퍼가 넘치지 않게
-
혼잡 제어 — 네트워크를 배려한다 — 중간 경로가 막히지 않게
-
실제 전송량 = min(수신 윈도우, 혼잡 윈도우)
- 둘 중 작은 쪽이 상한이다
작은 패킷 문제
Nagle 알고리즘 작은 데이터를 모아 한 번에 보낸다 (헤더 오버헤드를 줄인다) 대신 지연이 생긴다
지연 ACK ACK 를 조금 모았다 보낸다
둘이 겹치면 서로 기다려 수백 ms 가 새는 일이 있다
- 대화형·실시간 트래픽은 TCP_NODELAY 로 Nagle 을 끈다
면접 함정
- ❌ "흐름 제어와 혼잡 제어는 같은 것" → 배려 대상이 다르다(수신자 vs 네트워크).
- ❌ "윈도우가 크면 무조건 빠르다" → 네트워크가 감당 못 하면 혼잡 제어가 줄인다.
실제 값을 본다
ss -ti
# cubic wscale:7,7 rto:204 rtt:3.5/1.2 mss:1448
# cwnd:10 ssthresh:20 bytes_sent:1234567 retrans:0/3
# ^^^^^^ 혼잡 윈도우 ^^^^^^^^ 재전송 횟수
- wscale:7,7 — 윈도우 스케일 계수 — 실제 윈도우 = 광고값 × 2^7
- rtt:3.5/1.2 — 평균 RTT 3.5ms, 편차 1.2ms
- retrans — 재전송이 계속 늘면 회선이나 혼잡을 의심한다
버퍼 튜닝
sysctl net.ipv4.tcp_rmem # 수신 버퍼 min/default/max
sysctl net.ipv4.tcp_wmem # 송신 버퍼
sysctl net.ipv4.tcp_moderate_rcvbuf # 1이면 커널이 자동 조절한다
대부분은 자동 조절에 맡기는 게 낫다 — 손으로 키우면 메모리만 먹고 버퍼블로트를 유발할 수 있다. 장거리 고대역 전송처럼 BDP가 큰 경우에만 손댄다.
제로 윈도우 교착
수신자가 윈도우 0 을 광고한 뒤 Window Update 가 유실되면
- 송신자는 영원히 기다린다
- 그래서 송신자가 주기적으로 1바이트를 찔러 본다 (zero window probe)