Skip to Content

이번 문서의 목표: 이 파일을 다 읽으면 혼잡 윈도우(cwnd)가 느린 시작, 혼잡 회피, 손실 발생 시 각각 어떻게 변하는지 RTT 단위로 직접 계산할 수 있다.

왜 혼잡 제어가 필요한가

18편에서 흐름 제어(수신 윈도우)는 수신 측의 처리 능력을 보호하는 메커니즘이라고 배웠다. 그런데 수신 측이 아무리 여유가 있어도, 그 사이를 지나가는 라우터와 회선이 감당할 수 있는 양보다 더 많은 데이터를 보내면 어떻게 될까? 라우터의 버퍼가 넘쳐 패킷이 버려지고(패킷 손실), 네트워크 전체 성능이 급격히 떨어지는 혼잡 붕괴(congestion collapse)가 일어날 수 있다.

쉽게 말하면: 흐름 제어가 “상대방이 감당할 만큼만 보내는 것”이라면, 혼잡 제어(congestion control)는 “도로(네트워크) 자체가 막히지 않을 만큼만 보내는 것”이다.

문제는 송신 측이 네트워크 내부의 혼잡 상태를 직접 볼 방법이 없다는 것이다. 라우터 버퍼가 얼마나 찼는지, 다른 사용자가 얼마나 트래픽을 쓰는지 송신 측은 알 수 없다. TCP는 이 문제를 패킷 손실을 혼잡의 신호로 해석하는 방식으로 우회한다 — “패킷이 유실됐다는 것은 어딘가에서 버퍼가 넘쳤다는 뜻이니, 전송 속도를 줄이자”는 논리다.

혼잡 윈도우: 송신 측이 스스로 조절하는 값

혼잡 윈도우(cwnd, congestion window)는 송신 측이 확인 응답을 기다리지 않고 한 번에 보낼 수 있다고 스스로 추정하는 데이터의 양이다. 18편에서 배운 수신 윈도우(rwnd)와 이름은 비슷하지만 주체와 목적이 다르다.

  • rwnd(수신 윈도우) — 수신 측이 “내 버퍼가 이만큼 남았다”고 알려주는 값. 흐름 제어에 사용.
  • cwnd(혼잡 윈도우) — 송신 측이 “네트워크가 이 정도는 버텨줄 것 같다”고 스스로 추정하는 값. 혼잡 제어에 사용.

실제 송신 측이 한 번에 내보낼 수 있는 데이터 양은 이 둘 중 더 작은 값으로 결정된다.

실제 전송 가능량=min(cwnd,rwnd)\text{실제 전송 가능량} = \min(\text{cwnd}, \text{rwnd})

이 문서에서는 계산을 단순화하기 위해 cwnd와 ssthresh를 최대 세그먼트 크기(MSS, Maximum Segment Size)의 배수, 즉 정수 단위로 표현한다. 예를 들어 cwnd=4는 “한 번에 세그먼트 4개(4×MSS바이트)를 연속으로 보낼 수 있다”는 뜻이다.

1단계: 느린 시작(Slow Start)

왜 처음부터 크게 보내지 않는가

연결이 갓 시작된 시점에는 송신 측이 네트워크 상태를 전혀 모른다. 그래서 TCP는 아주 작은 값(cwnd=1)에서 시작해 RTT마다 cwnd를 2배로 늘려간다. 이름은 “느린 시작”이지만 실제로는 지수적으로 빠르게 증가한다 — “시작할 때는 조심스럽게 1부터 출발한다”는 뜻으로 이해하면 된다.

쉽게 말하면: 낯선 길을 처음 운전할 때 천천히 출발했다가 문제가 없으면 점점 속도를 배로 올리는 것과 같다.

RTT(Round-Trip Time, 왕복 시간)는 세그먼트를 보내고 그에 대한 확인 응답이 돌아오기까지 걸리는 시간이다. cwnd 조정은 시간(초) 단위가 아니라 이 RTT를 기준 단위로 이루어진다는 점이 중요하다 — RTT 한 번은 “확인 응답 한 묶음을 받는 주기”를 의미한다.

느린 시작에서 cwnd가 2배씩 느는 이유는, 한 RTT 동안 cwnd개의 세그먼트를 보내면 그만큼의 확인 응답(ACK)이 돌아오는데, 각 ACK마다 cwnd를 1씩 늘리는 규칙 때문이다. cwnd개의 ACK가 도착하면 cwnd가 cwnd만큼 늘어나 결과적으로 두 배(cwnd + cwnd = 2×cwnd)가 된다.

언제 느린 시작을 멈추는가

송신 측은 혼잡 임계값(ssthresh, slow start threshold)이라는 값을 별도로 관리한다. cwnd가 ssthresh에 도달하면 느린 시작을 멈추고 다음 단계인 혼잡 회피로 전환한다. 이번 예시에서는 ssthresh의 초기값을 16으로 둔다.

2단계: 혼잡 회피(Congestion Avoidance)

cwnd가 ssthresh에 도달한 뒤에는 더 이상 2배씩 늘리지 않는다. 대신 RTT마다 1씩만 더한다. 지수적 증가에서 선형적 증가로 속도를 크게 줄이는 것이다.

쉽게 말하면: 목적지 근처에 다다르면 과속방지턱을 넘듯 속도를 조심스럽게 올리는 구간이다.

이렇게 조심스럽게 늘리는 이유는, ssthresh 근처는 이전에 혼잡이 감지됐던 지점에 가까워지는 구간이므로 다시 손실이 날 위험이 커지기 때문이다. 손실이 나기 직전까지 최대한 안전하게 전송량을 탐색하는 것이 혼잡 회피의 목적이다.

3단계: 손실 감지와 AIMD

혼잡 회피 중에도 언젠가는 실제로 네트워크가 혼잡해져 패킷 손실이 발생한다. 이때 TCP가 반응하는 방식을 AIMD(Additive Increase Multiplicative Decrease, 덧셈 증가·곱셈 감소)라고 부른다.

  • 덧셈 증가(Additive Increase) — 손실이 없는 동안은 혼잡 회피 구간에서 RTT마다 cwnd를 1씩 더한다.
  • 곱셈 감소(Multiplicative Decrease) — 손실이 감지되면 cwnd를 절반으로 곱해(×0.5) 줄인다.

손실을 감지하는 방법에 따라 반응 강도가 다르다.

  • 빠른 재전송으로 감지(중복 ACK 3번) — 네트워크가 완전히 멈춘 것은 아니라고 판단해, ssthresh를 현재 cwnd의 절반으로 낮추고 cwnd도 그 값으로 줄인 뒤 혼잡 회피부터 다시 시작한다(빠른 회복, fast recovery).
  • 타임아웃으로 감지(확인 응답이 아예 오지 않음) — 네트워크 상태를 전혀 알 수 없는 심각한 상황으로 판단해, ssthresh를 현재 cwnd의 절반으로 낮추고 cwnd는 1로 완전히 초기화해 느린 시작부터 다시 시작한다.

쉽게 말하면: 중복 ACK로 눈치채면 “속도만 줄이고 계속 달리기”, 타임아웃이면 “아예 멈췄다가 처음부터 다시 출발하기”다.

cwnd 값 변화표: RTT마다 직접 계산

이제 ssthresh 초기값 16, cwnd 초기값 1로 두고 RTT마다 cwnd가 어떻게 변하는지 끝까지 계산한다. 10번째 RTT에서 중복 ACK 3번으로 손실이 감지된다고 가정한다.

느린 시작 구간 (RTT 1~5)

cwnd가 2배씩 늘어 ssthresh인 16에 도달할 때까지다.

cwndn+1=cwndn×2\text{cwnd}_{n+1} = \text{cwnd}_n \times 2
RTT단계계산cwnd(세그먼트 수)
1느린 시작시작값1
2느린 시작1 × 22
3느린 시작2 × 24
4느린 시작4 × 28
5느린 시작8 × 216

RTT 5에서 cwnd가 ssthresh(16)에 도달했으므로, RTT 6부터는 혼잡 회피로 전환한다.

혼잡 회피 구간 (RTT 6~10)

cwnd가 RTT마다 1씩만 늘어난다.

cwndn+1=cwndn+1\text{cwnd}_{n+1} = \text{cwnd}_n + 1
RTT단계계산cwnd(세그먼트 수)
6혼잡 회피16 + 117
7혼잡 회피17 + 118
8혼잡 회피18 + 119
9혼잡 회피19 + 120
10혼잡 회피 중 손실 감지(중복 ACK 3번)20 + 121 (손실 직전 도달값)

손실 감지 이후 (RTT 10 → RTT 11, 빠른 회복)

RTT 10에서 cwnd=21에 도달한 시점에 중복 ACK 3번으로 손실이 감지됐다. 곱셈 감소를 적용해 새로운 ssthresh와 cwnd를 계산한다.

ssthreshnew=cwnd2=212=10.510\text{ssthresh}_{\text{new}} = \frac{\text{cwnd}}{2} = \frac{21}{2} = 10.5 \rightarrow 10

세그먼트는 소수 단위로 나눌 수 없으므로 소수점 이하는 버림(내림) 처리해 정수로 맞춘다.

cwndnew=ssthreshnew=10\text{cwnd}_{\text{new}} = \text{ssthresh}_{\text{new}} = 10

빠른 재전송으로 감지했으므로 cwnd는 1로 초기화되지 않고, 곧바로 새 ssthresh 값인 10에서 혼잡 회피를 재개한다.

RTT단계계산cwnd(세그먼트 수)ssthresh
10(손실 직후)곱셈 감소21 ÷ 2 = 10.5 → 101010
11혼잡 회피 재개10 + 11110
12혼잡 회피11 + 11210

만약 타임아웃으로 손실을 감지했다면

같은 RTT 10, cwnd=21 시점에 중복 ACK가 아니라 타임아웃으로 손실을 감지했다면 계산이 달라진다. ssthresh는 똑같이 cwnd의 절반(10)으로 낮추지만, cwnd 자체는 1로 완전히 초기화하고 느린 시작부터 다시 밟는다.

RTT단계계산cwnd(세그먼트 수)ssthresh
10(타임아웃 직후)cwnd 초기화1로 리셋110
11느린 시작1 × 2210
12느린 시작2 × 2410
13느린 시작4 × 2810
14느린 시작 → 혼잡 회피 전환8 × 2 = 16이나 ssthresh(10) 초과 예정이므로 10에서 전환1010

여기서 주의할 점은, 느린 시작 중이라도 cwnd가 ssthresh를 넘어서는 순간 즉시 혼잡 회피로 전환한다는 것이다. RTT 13에서 cwnd=8이고 다음 배수(16)가 ssthresh(10)를 넘으므로, RTT 14에서는 8을 2배로 늘리는 대신 ssthresh 지점인 10까지만 늘리고 혼잡 회피 모드로 넘어간다.

전체 흐름 그래프로 보는 cwnd 변화

이 표와 그래프에서 확인할 수 있듯, cwnd는 톱니 모양(sawtooth)으로 움직인다. 손실이 나기 전까지는 계속 증가하다가(처음엔 지수적으로, 이후엔 선형적으로), 손실이 나는 순간 뚝 떨어진 뒤 다시 증가를 반복한다. 이 톱니 모양 그래프는 TCP 혼잡 제어를 설명하는 대표적인 그림으로 시험에도 자주 인용된다.

자주 틀리는 점

  • “느린 시작이라서 계속 느리게 증가한다”는 착각 — 이름과 달리 느린 시작 구간은 RTT마다 2배씩, 즉 지수적으로 빠르게 증가한다. “느리다”는 것은 cwnd=1이라는 시작점이 조심스럽다는 뜻이지, 증가 속도가 느리다는 뜻이 아니다.
  • 혼잡 회피가 감소 단계라는 착각 — 혼잡 회피도 cwnd가 계속 증가하는 단계다. 다만 그 증가 폭이 1(선형)로 줄어들 뿐이다.
  • 손실 시 항상 cwnd가 1로 초기화된다는 착각 — 빠른 재전송(중복 ACK)으로 감지했을 때는 cwnd를 1이 아니라 절반으로만 줄이고 혼잡 회피를 재개한다. cwnd가 1로 완전히 초기화되는 것은 타임아웃으로 감지했을 때뿐이다.
  • ssthresh와 cwnd를 같은 값으로 혼동 — ssthresh는 “느린 시작을 멈추는 기준선”이고 cwnd는 “지금 실제로 보낼 수 있는 양”이다. 손실 후에는 두 값이 같아지는 순간이 있을 뿐, 항상 같은 값은 아니다.
  • cwnd 단위를 RTT(시간)와 혼동 — cwnd는 “한 번에 보낼 수 있는 세그먼트 개수”이고, RTT는 “그 세그먼트들에 대한 확인 응답이 돌아오는 주기”다. 서로 다른 종류의 값이다.

핵심 정리

  • 혼잡 제어는 송신 측이 스스로 추정하는 혼잡 윈도우(cwnd)로 네트워크 경로 전체의 혼잡을 방지하는 메커니즘이며, 흐름 제어(rwnd)와는 조절 대상이 다르다.
  • 느린 시작은 cwnd=1에서 시작해 RTT마다 2배씩(지수적) 증가하다가 ssthresh에 도달하면 혼잡 회피로 전환한다.
  • 혼잡 회피는 RTT마다 1씩(선형) 증가하며 손실이 나기 직전까지 조심스럽게 전송량을 늘린다.
  • AIMD 원칙에 따라 손실이 감지되면 ssthresh를 현재 cwnd의 절반으로 낮추고, 빠른 재전송이면 cwnd를 그 절반 값으로, 타임아웃이면 cwnd를 1로 초기화한다.
  • 이 반복이 cwnd의 톱니 모양(sawtooth) 그래프를 만들며, 이는 TCP가 네트워크 용량을 계속 탐색하면서도 손실 시 즉각 물러서는 방식으로 안정성을 유지함을 보여준다.

마무리 복습

문제 14지선다
TCP 느린 시작(Slow Start) 단계에 대한 설명으로 옳은 것은?
문제 24지선다
ssthresh=16, cwnd=1로 느린 시작을 시작했을 때, cwnd가 처음으로 16에 도달하는 시점은 몇 번째 RTT인가?
문제 34지선다
혼잡 회피(Congestion Avoidance) 단계에서 cwnd=19인 상태로 다음 RTT를 맞이했다. 손실 없이 정상적으로 진행됐다면 다음 RTT의 cwnd 값은?
문제 44지선다
cwnd=21일 때 중복 ACK 3번(빠른 재전송)으로 손실이 감지됐다. AIMD 규칙에 따른 새로운 ssthresh와 cwnd 값으로 옳은 것은?
문제 54지선다
같은 cwnd=21 시점에 중복 ACK가 아니라 타임아웃으로 손실을 감지했다면, 빠른 재전송으로 감지한 경우와 비교했을 때 가장 정확한 차이는?
문제 64지선다
TCP 혼잡 윈도우(cwnd)의 시간에 따른 변화 그래프가 톱니 모양(sawtooth)으로 나타나는 이유로 가장 적절한 것은?

참고 자료

Last updated on