TCP 혼잡 제어는 송신자가 네트워크 상태를 직접 알 수 없다는 데서 출발한다. 송신자는 ACK, 손실, RTT, ECN 같은 신호를 보고 한 번에 네트워크로 내보낼 데이터의 양인 **혼잡 윈도우(cwnd)**를 조절한다. 핵심 흐름은 slow start → congestion avoidance → 손실 감지 → fast retransmit/recovery 또는 timeout이다.
기존 노트는 Tahoe·Reno·NewReno와 영어 표현을 한데 모아 두어, 실제로 패킷이 오갈 때 cwnd가 왜 변하는지 따라가기 어려웠다. 이번에는 하나의 TCP 연결에서 일어나는 순서로 다시 정리했다.
실제 전송량은 cwnd 하나로 정해지지 않는다
송신자가 ACK 없이 보낼 수 있는 미확인 데이터의 상한은 두 윈도우 가운데 작은 값으로 제한된다.
전송 가능량 = min(cwnd, rwnd)
cwnd는 송신자가 추정한 네트워크 수용량이다. 혼잡 제어가 조절한다.rwnd는 수신 버퍼의 여유다. 흐름 제어가 조절한다.
따라서 cwnd가 커도 수신 윈도우가 작으면 더 보낼 수 없다. 반대로 rwnd가 충분해도 네트워크가 혼잡하다고 판단하면 cwnd가 전송량을 제한한다.
1단계: slow start로 경로 용량을 탐색한다
새 연결은 경로가 얼마나 많은 데이터를 감당할지 모른다. slow start는 작은 초기 cwnd에서 시작해 새 데이터를 확인하는 ACK가 올 때마다 cwnd를 늘린다. 결과적으로 손실이 없다면 대략 RTT마다 윈도우가 두 배로 커지는 구간이 생긴다.
RTT 0: 1개 분량
RTT 1: 2개 분량
RTT 2: 4개 분량
RTT 3: 8개 분량
이름은 “slow”지만 증가는 지수적이다. 처음부터 큰 burst를 보내지 않고 ACK clock을 만들며 경로 용량을 빠르게 탐색한다는 뜻에 가깝다.
2단계: congestion avoidance에서 조심스럽게 늘린다
cwnd가 slow start threshold인 ssthresh에 도달하면 혼잡 회피 구간으로 넘어간다. 고전적인 Reno 동작에서는 ACK를 모아 대략 RTT마다 한 MSS만큼 cwnd를 늘린다. 그래프는 서서히 증가하다 손실 신호에서 감소하는 톱니 모양이 된다.
이 패턴이 AIMD다.
- Additive Increase: 혼잡이 보이지 않으면 선형적으로 증가한다.
- Multiplicative Decrease: 혼잡 신호를 받으면 비율로 크게 줄인다.
다만 모든 현대 TCP가 Reno의 선형 증가식을 그대로 쓰는 것은 아니다. CUBIC처럼 시간의 3차 함수를 이용하는 알고리즘도 있고, 운영체제의 설정과 연결마다 선택이 달라질 수 있다.
손실은 duplicate ACK와 timeout으로 감지한다
세 번째 duplicate ACK: fast retransmit
중간 세그먼트 하나가 빠지고 그 뒤의 세그먼트가 계속 도착하면 수신자는 같은 ACK 번호를 반복해 보낸다. 송신자가 세 번째 duplicate ACK를 받으면 일반적으로 재전송 타이머가 끝나기 전에 누락된 세그먼트를 다시 보낸다. 이것이 fast retransmit이다.
duplicate ACK가 온다는 것은 뒤의 세그먼트가 수신자에게 도착하고 있다는 뜻이기도 하다. 경로가 완전히 멈춘 것은 아니므로 Reno 계열은 cwnd를 줄인 뒤 fast recovery로 들어가 ACK clock을 유지한다.
RTO 만료: 더 강한 혼잡 신호
ACK가 충분히 오지 않아 retransmission timeout(RTO)이 만료되면 가장 오래 확인되지 않은 세그먼트를 재전송한다. RTO는 측정한 RTT의 평활값인 SRTT와 변동값 RTTVAR로 계산하며, 반복 만료 시 타이머를 지수적으로 늘린다.
RTO 만료 → 미확인 세그먼트 재전송 → RTO를 2배로 backoff
타임아웃은 duplicate ACK 기반 손실보다 경로 상태가 나쁘다는 신호로 취급되어 cwnd를 더 보수적으로 줄이고 slow start부터 다시 탐색한다.
fast recovery의 window inflation은 오류가 아니다
기존 노트에는 window inflation을 “cwnd가 실제보다 커져 과도한 혼잡을 만드는 현상”으로 적었다. Reno fast recovery에서는 의도된 동작이다.
손실 뒤에 도착한 각 duplicate ACK는 세그먼트 하나가 네트워크를 빠져나갔다는 단서다. 송신자는 duplicate ACK 수에 맞춰 cwnd를 일시적으로 부풀리고 새 데이터를 보낼 수 있다. 누락 구간을 모두 확인하는 ACK가 오면 cwnd를 ssthresh 수준으로 줄여 혼잡 회피로 돌아간다.
구현 버그나 ACK 조작은 별개의 문제지만, fast recovery 절차의 window inflation 자체를 오류로 보면 안 된다.
Tahoe·Reno·NewReno는 무엇이 다른가
| 방식 | duplicate ACK로 손실을 찾은 뒤 | 여러 손실이 있을 때 |
|---|---|---|
| Tahoe | fast retransmit 후 cwnd를 크게 줄이고 slow start | 복구가 느리다. |
| Reno | fast retransmit와 fast recovery 사용 | 한 윈도우에서 여러 손실이 생기면 불리하다. |
| NewReno | partial ACK를 이용해 recovery를 이어 간다. | SACK 없이도 Reno보다 여러 손실에 잘 대응한다. |
이 이름들은 혼잡 제어의 발전 과정을 이해하는 데 유용하지만, 현재 시스템을 분석할 때는 실제 알고리즘과 기능을 확인해야 한다. Linux에서는 다음 명령으로 기본값과 사용 가능한 알고리즘을 볼 수 있다.
sysctl net.ipv4.tcp_congestion_control
sysctl net.ipv4.tcp_available_congestion_control
처리량 근사식은 언제 쓸 수 있는가
고전적인 Reno 계열의 정상 상태를 단순화하면 처리량이 대략 다음 관계를 가진다고 설명할 수 있다.
throughput ∝ MSS / (RTT × √p)
p는 손실 확률이다. RTT가 길거나 손실이 잦을수록 ACK에 맞춰 윈도우를 키우는 속도가 느려진다는 직관을 준다. 하지만 타임아웃, 수신 윈도우, 애플리케이션 전송 패턴, SACK, pacing, CUBIC·BBR 같은 다른 알고리즘을 생략한 근사다. 실제 링크 대역폭을 이 식 하나로 예측해서는 안 된다.
패킷 캡처와 시스템에서 확인할 값
혼잡 제어 문제를 볼 때는 다음을 한 흐름으로 확인한다.
- 재전송과 duplicate ACK가 언제 시작됐는가
- RTT와 RTO가 어떻게 변했는가
- 수신 윈도우 제한인지 cwnd 제한인지
- ECN 표시나 패킷 손실이 있는지
- 현재 호스트가 어떤 혼잡 제어 알고리즘을 쓰는지
HTTP 연결이 왜 유지되고 재사용되는지까지 이어서 보려면 HTTP Keep-Alive와 연결 성능을 참고할 수 있다. 지연을 “네트워크가 느리다”로 뭉뚱그리지 않고 ACK와 윈도우의 변화로 나누는 것이 이 개념을 공부하며 가장 크게 달라진 지점이었다.
참고 자료
'배움과 성장 > 네트워크·보안' 카테고리의 다른 글
| 네트워크 공격을 방어 관점에서 분류하기: 도청·위조·가용성 (2) | 2025.09.02 |
|---|---|
| 라우팅과 포워딩 차이: RIP·OSPF·BGP가 경로를 만드는 방식 (13) | 2025.08.25 |
| Kubernetes 네트워크 트러블슈팅 순서: DNS·TCP·TLS·HTTP를 증거로 좁히기 (0) | 2025.03.25 |
| TCP·UDP 기초: Handshake·Socket State·Routing·Subnet을 한 흐름으로 이해하기 (0) | 2025.02.23 |
| 실시간 통신 방식 비교: 폴링·SSE·WebSocket·모바일 푸시 선택 기준 (2) | 2024.12.05 |
댓글