에듀테크 프로젝트 대시보드
TCP 흐름 제어 & 혼잡 제어
TCP는 데이터를 "보낼 수 있는 만큼"만 보낸다. 받는 쪽이 감당할 양(흐름 제어)과 네트워크가 감당할 양(혼잡 제어)을 계속 재면서, 창(window) 크기를 늘렸다 줄였다 한다. 그 결과가 바로 유명한 톱니파(sawtooth) 그래프다.
송신자가 ACK를 받지 않고 미리 보낼 수 있는 바이트 수를 전송 윈도우라 한다. 이 값은 두 제한의 최솟값이다.
| 이름 | 누가 정하나 | 막으려는 것 |
|---|---|---|
rwnd (수신 윈도우) | 수신자가 ACK 헤더에 담아 알려줌 | 수신 버퍼 넘침 (흐름 제어) |
cwnd (혼잡 윈도우) | 송신자가 스스로 추정 | 네트워크 라우터 큐 넘침 (혼잡 제어) |
보낼 수 있는 양 = min(rwnd, cwnd)
사용 중(in-flight) = 마지막에 보낸 바이트 − 마지막으로 ACK된 바이트
남은 여유 = min(rwnd, cwnd) − 사용 중
아래 데모는 네트워크가 병목이라고 보고 cwnd에 집중한다. 단위는 편의상 MSS(세그먼트) 개수로 센다.
세그먼트를 왼쪽부터 순서대로 보낸다. 왼쪽 끝 세그먼트가 ACK되면 창 전체가 오른쪽으로 미끄러지며 새 세그먼트를 보낼 자리가 생긴다. 창 크기(cwnd)만큼만 "날아다니는" 상태로 유지된다.
TCP는 두 국면을 오간다.
cwnd를 2배로. 이름은 "느린"이지만 실제로는 지수적 증가다(작게 시작한다는 뜻). cwnd < ssthresh일 때.cwnd를 +1만. 선형 증가로 조심스럽게 한계를 탐색. cwnd ≥ ssthresh일 때.손실이 감지되면(AIMD의 MD: Multiplicative Decrease):
ssthresh = cwnd/2, cwnd = ssthresh 로 절반만 줄이고 혼잡 회피로 (Fast Recovery, TCP Reno).ssthresh = cwnd/2, cwnd = 1 로 바닥부터 느린 시작 다시."1 RTT 진행"을 누를 때마다 규칙에 따라 cwnd가 자란다. 빨간 버튼으로 손실을 일으켜 창이 반토막 나는 걸 확인해 보자. 여러 번 반복하면 교과서에 나오는 그 톱니 모양이 그려진다.
# RTT마다 한 번 호출된다고 가정 (MSS 단위) on_ack(): if cwnd < ssthresh: cwnd += cwnd # 느린 시작: ×2 (지수) else: cwnd += 1 # 혼잡 회피: +1 (선형) on_triple_dup_ack(): # 가벼운 손실 ssthresh = cwnd / 2 cwnd = ssthresh # 절반으로, 회피 국면 유지 on_timeout(): # 심각한 손실 ssthresh = cwnd / 2 cwnd = 1 # 바닥부터 다시 느린 시작
실제 커널은 바이트 단위로, ACK 하나하나마다 cwnd += MSS·MSS/cwnd 같은 식으로 잘게 쪼개 적용한다. 개념은 같다.
여러 흐름이 한 링크를 나눠 쓸 때, 덧셈 증가 · 곱셈 감소는 대역폭을 공평하게 수렴시킨다. 많이 쓰던 흐름일수록 손실 때 더 많이(절대량으로) 깎이기 때문이다. 만약 둘 다 곱셈이거나 둘 다 덧셈이면 공평점으로 모이지 않는다. 이것이 인터넷이 지금까지 안 무너진 이유 중 하나다.
다운로드 속도가 처음엔 쭉 오르다(느린 시작) 중간에 툭툭 떨어지는 게 바로 손실 → cwnd 감소.
무선은 잡음으로도 패킷이 깨진다. TCP는 이를 "혼잡"으로 오해해 속도를 줄이기도 한다.
RTT가 μs 단위라 DCTCP처럼 ECN(라우터가 큐 상태를 표시)을 쓰는 특화 알고리즘을 적용.
요즘 서버는 cwnd=10으로 시작해 작은 웹 페이지는 1 RTT에 다 받도록 한다.