본문 바로가기
TYLER SONGBlog
← Blog index
CS10 min read

Congestion Control: 혼잡 윈도우로 네트워크 과부하를 조절하는 TCP 전송 제어

혼잡 제어(Congestion Control)는 송신자가 네트워크 상태에 맞춰 전송량을 조절하는 과정이다. TCP는 혼잡 윈도우(Congestion Window)를 늘리며 가용 대역폭을 탐색하고, 패킷 손실이나 명시적 혼잡 알림을 감지하면 전송량을 줄인다. 흐름 제어(Flow Control)가 수신자의 처리 능력을 보호한다면 혼잡 제어는 네트워크 경로의 과

By 송민성

1. 개념

혼잡 제어(Congestion Control)는 송신자가 네트워크 경로의 수용 능력을 넘지 않도록 데이터 전송량을 조절하는 기법이다.

네트워크 혼잡(Network Congestion)은 라우터로 들어오는 트래픽이 처리하거나 전달할 수 있는 양보다 많을 때 발생한다. 혼잡이 심해지면 라우터의 큐가 길어지고, 큐가 가득 차면 패킷이 버려진다.

text
송신자 ──→ 라우터 큐 ──→ 수신자
             ↓
        처리량 초과
             ↓
      지연 증가와 패킷 손실

TCP 송신자는 혼잡 윈도우(Congestion Window, cwnd)를 사용해 확인 응답 없이 네트워크에 보낼 수 있는 데이터양을 제한한다.

실제 전송 가능한 데이터양은 혼잡 윈도우와 수신 윈도우(Receive Window, rwnd) 중 작은 값으로 결정된다.

text
전송 가능량 = min(cwnd, rwnd)

두 윈도우의 목적은 다르다.

  • 혼잡 윈도우(cwnd): 네트워크 과부하 방지
  • 수신 윈도우(rwnd): 수신 버퍼 초과 방지
  • cwnd: 송신자가 혼잡 상태를 추정해 관리
  • rwnd: 수신자가 TCP 헤더를 통해 송신자에게 전달

2. 왜 사용하는가

여러 송신자가 네트워크 용량을 고려하지 않고 계속 패킷을 보내면 라우터 큐가 가득 찬다. 손실된 패킷을 다시 보내면서 트래픽이 더 증가할 수도 있다.

text
전송량 증가
   ↓
라우터 큐 증가
   ↓
지연과 패킷 손실 증가
   ↓
재전송 증가
   ↓
혼잡 악화

이 현상이 심해져 재전송은 많지만 유효한 데이터가 거의 전달되지 않는 상태를 혼잡 붕괴(Congestion Collapse)라고 한다.

혼잡 제어는 다음 목표를 가진다.

  • 네트워크의 가용 대역폭 탐색
  • 과도한 큐 대기와 패킷 손실 억제
  • 여러 연결 사이의 전송 기회 분배
  • 혼잡 발생 후 전송률 복구
  • 변화하는 네트워크 상태 적응

TCP는 네트워크 전체 상태를 직접 알 수 없다. 확인 응답(ACK), 왕복 시간(Round-Trip Time, RTT), 패킷 손실, 명시적 혼잡 알림(Explicit Congestion Notification, ECN) 같은 신호로 혼잡을 추정한다.

3. 동작 원리

느린 시작

느린 시작(Slow Start)은 작은 혼잡 윈도우에서 출발해 전송량을 빠르게 늘리는 단계다.

확인 응답이 정상적으로 도착하면 각 ACK마다 cwnd를 늘린다. 결과적으로 손실이 없다는 가정에서 cwnd는 RTT마다 대략 두 배로 증가한다.

text
RTT 1: 1 MSS
RTT 2: 2 MSS
RTT 3: 4 MSS
RTT 4: 8 MSS
RTT 5: 16 MSS

최대 세그먼트 크기(Maximum Segment Size, MSS)는 TCP 세그먼트 하나에 담을 수 있는 데이터의 최대 크기다.

느린 시작은 이름과 달리 지수적으로 증가한다. 여기서 느리다는 표현은 초기부터 전체 용량으로 전송하지 않는다는 뜻이다.

느린 시작 임계값

느린 시작 임계값(Slow Start Threshold, ssthresh)은 느린 시작과 혼잡 회피를 구분하는 기준이다.

text
cwnd < ssthresh  → 느린 시작
cwnd ≥ ssthresh  → 혼잡 회피

cwnd가 ssthresh에 도달하면 증가 속도를 낮춰 네트워크 용량을 탐색한다.

혼잡 회피

혼잡 회피(Congestion Avoidance)는 혼잡 윈도우를 선형적으로 증가시키는 단계다.

고전적인 TCP Reno의 혼잡 회피에서는 한 RTT 동안 cwnd가 대략 1 MSS 증가한다.

text
16 MSS → 17 MSS → 18 MSS → 19 MSS

전송량을 천천히 늘리다가 혼잡 신호를 발견하면 크게 줄이는 방식을 가산 증가·승산 감소(Additive Increase Multiplicative Decrease, AIMD)라고 한다.

text
혼잡 없음: 일정량 증가
혼잡 발생: 일정 비율로 감소

재전송 타임아웃

재전송 타임아웃(Retransmission Timeout, RTO)은 일정 시간 안에 ACK가 도착하지 않았음을 뜻한다. TCP Reno는 이를 강한 혼잡 신호로 취급한다.

개념적인 반응은 다음과 같다.

text
ssthresh = 기존 cwnd / 2
cwnd = 초기 윈도우
느린 시작 재개

현대 TCP의 정확한 초기 혼잡 윈도우와 타이머 계산은 구현 및 표준에 따라 결정된다. 핵심은 타임아웃 발생 시 전송량을 크게 줄인다는 점이다.

빠른 재전송과 빠른 복구

송신자가 같은 ACK 번호를 반복해서 받으면 중간 세그먼트가 손실됐다고 추정할 수 있다.

TCP Reno는 일반적으로 중복 ACK(Duplicate ACK) 세 개를 받으면 타임아웃을 기다리지 않고 빠른 재전송(Fast Retransmit)을 수행한다.

text
세그먼트 1 수신 → ACK 2
세그먼트 2 손실
세그먼트 3 수신 → ACK 2
세그먼트 4 수신 → ACK 2
세그먼트 5 수신 → ACK 2
                    ↑
                중복 ACK 3개

이후 빠른 복구(Fast Recovery)를 통해 cwnd를 줄이되, 타임아웃처럼 전송을 처음부터 다시 시작하지는 않는다. 세부 계산은 TCP 구현과 알고리즘에 따라 달라진다.

혼잡 제어 상태 흐름

text
연결 시작
   ↓
느린 시작
   ↓ cwnd가 ssthresh 도달
혼잡 회피
   ↓ 손실 감지
cwnd와 ssthresh 감소
   ↓
느린 시작 또는 빠른 복구

혼잡 제어 알고리즘

TCP 혼잡 제어는 하나의 고정된 알고리즘이 아니다.

  • TCP Reno: 손실을 주요 혼잡 신호로 사용하는 AIMD 기반 알고리즘
  • CUBIC: 시간을 기준으로 삼차 함수 형태로 cwnd를 조절하는 알고리즘
  • BBR: 병목 대역폭과 최소 RTT를 추정해 전송 속도를 조절하는 알고리즘

운영체제와 네트워크 환경에 따라 사용하는 알고리즘이 달라진다.

4. 코드 예제

다음 코드는 TCP Reno의 핵심 동작을 RTT 단위로 단순화한 시뮬레이션이다. 실제 TCP는 ACK 단위 증가, RTT 추정, 재전송 타이머, 빠른 복구 상태를 더 세밀하게 처리한다.

typescript
type CongestionState = "느린 시작" | "혼잡 회피"; type RoundResult = { round: number; state: CongestionState; cwndBefore: number; cwndAfter: number; ssthresh: number; event: string; }; class RenoSimulator { private cwnd = 1; private ssthresh = 16; runRound(round: number, loss: boolean): RoundResult { const cwndBefore = this.cwnd; const state: CongestionState = this.cwnd < this.ssthresh ? "느린 시작" : "혼잡 회피"; if (loss) { this.ssthresh = Math.max(Math.floor(this.cwnd / 2), 2); this.cwnd = 1; return { round, state, cwndBefore, cwndAfter: this.cwnd, ssthresh: this.ssthresh, event: "타임아웃 감지: 윈도우 감소", }; } if (state === "느린 시작") { this.cwnd *= 2; } else { this.cwnd += 1; } return { round, state, cwndBefore, cwndAfter: this.cwnd, ssthresh: this.ssthresh, event: "ACK 정상 수신", }; } } const simulator = new RenoSimulator(); const lossRounds = new Set([6, 13]); const results: RoundResult[] = []; for (let round = 1; round <= 16; round += 1) { results.push(simulator.runRound(round, lossRounds.has(round))); } console.table(results);

tsx가 설치된 환경에서 실행한다.

shell
npx tsx congestion-control.ts

초기 흐름은 다음과 같다.

text
1 → 2 → 4 → 8 → 16

6번째 RTT에서 손실이 발생하면 ssthresh는 기존 cwnd의 절반으로 줄고, cwnd는 1로 돌아간다. 이후 다시 느린 시작을 거쳐 혼잡 회피로 전환된다.

리눅스에서는 현재 사용 중인 TCP 혼잡 제어 알고리즘을 확인할 수 있다.

shell
sysctl net.ipv4.tcp_congestion_control sysctl net.ipv4.tcp_available_congestion_control

소켓별 TCP 상태는 ss 명령으로 확인한다.

shell
ss -tin

출력에는 환경에 따라 혼잡 제어 알고리즘, cwnd, RTT, 재전송 관련 정보가 표시된다.

5. 시간 복잡도 또는 성능 특성

혼잡 제어는 입력 크기를 처리하는 일반 알고리즘과 달리 ACK와 타이머 이벤트에 반응하는 제어 과정이다.

일반적인 윈도우 갱신은 이벤트 한 번당 고정된 수의 상태값을 계산하므로 다음과 같이 볼 수 있다.

text
ACK 또는 손실 이벤트당 계산 시간: O(1)
연결당 혼잡 제어 상태 공간: O(1)

전송 성능은 시간 복잡도보다 다음 요소의 영향을 크게 받는다.

  • 왕복 시간(RTT)
  • 패킷 손실률
  • 병목 링크의 대역폭
  • 라우터 큐 크기
  • 수신 윈도우
  • 혼잡 제어 알고리즘

대역폭-지연 곱(Bandwidth-Delay Product, BDP)은 경로를 가득 활용하는 데 필요한 비행 중 데이터양을 나타낸다.

text
BDP = 병목 대역폭 × RTT

예를 들어 병목 대역폭이 초당 100메가비트이고 RTT가 100밀리초라면 다음과 같다.

text
100,000,000 bit/s × 0.1 s
= 10,000,000 bit
= 1,250,000 byte

이 경로를 충분히 활용하려면 흐름 제어와 혼잡 제어가 허용하는 비행 중 데이터양이 약 1.25MB 이상이어야 한다.

손실 기반 알고리즘은 혼잡을 확인한 뒤 윈도우를 줄인다. 라우터 버퍼가 지나치게 크면 패킷 손실 전까지 큐가 길어져 지연이 크게 증가할 수 있다. 이를 버퍼블로트(Bufferbloat)라고 한다.

6. 실무 사용 사례

대용량 파일 전송

클라우드 저장소 업로드, 백업, 소프트웨어 배포는 연결을 오래 유지한다. 초기에는 느린 시작으로 대역폭을 탐색하고, 이후 혼잡 회피 단계에서 전송률을 조절한다.

RTT가 큰 경로에서는 윈도우가 충분히 커지는 데 시간이 필요하다. 짧은 연결을 반복하는 것보다 연결을 재사용하는 편이 효율적일 수 있다.

HTTP/2와 HTTP/3

HTTP/2는 하나의 TCP 연결에서 여러 스트림을 다중화(Multiplexing)한다. 해당 연결의 모든 스트림이 같은 TCP 혼잡 제어 상태를 공유한다.

HTTP/3는 QUIC 위에서 동작한다. QUIC은 UDP를 전송 기반으로 사용하지만 자체적으로 신뢰성, 손실 복구, 혼잡 제어를 제공한다. 여러 스트림을 독립적으로 처리해 TCP 연결 수준의 헤드 오브 라인 블로킹(Head-of-Line Blocking)을 줄인다.

데이터베이스 복제

데이터베이스 복제와 로그 전송은 지속적인 네트워크 처리량이 필요하다. 혼잡 제어가 전송률을 낮추면 복제 지연이 증가할 수 있다.

애플리케이션 지표만 확인하지 말고 RTT, 재전송, 패킷 손실, 전송 큐도 함께 관찰해야 한다.

실시간 서비스

음성 통화, 영상 회의, 게임은 처리량뿐 아니라 지연과 지터(Jitter)에 민감하다. 애플리케이션은 전송률 조절, 비트레이트 변경, 패킷 우선순위 처리 등을 전송 계층의 혼잡 제어와 함께 사용한다.

콘텐츠 전송 네트워크

콘텐츠 전송 네트워크(Content Delivery Network, CDN)는 사용자의 물리적·네트워크적 위치와 가까운 서버에서 콘텐츠를 제공한다. RTT가 줄면 ACK가 더 빨리 돌아오고 혼잡 윈도우 증가와 손실 복구도 빨라진다.

7. 주의할 점

혼잡 제어와 흐름 제어는 다르다

수신자가 느린 상황과 네트워크가 혼잡한 상황은 원인이 다르다.

text
흐름 제어: 수신 버퍼 보호
혼잡 제어: 네트워크 경로 보호

실제 송신량은 두 제한을 동시에 받는다.

패킷 손실이 항상 혼잡을 뜻하지는 않는다

유선 네트워크에서는 큐 초과가 주요 손실 원인이 될 수 있다. 무선 네트워크에서는 신호 간섭이나 전파 상태 때문에 패킷이 손실될 수도 있다.

손실 기반 알고리즘은 원인과 무관하게 전송량을 줄일 수 있다.

처리량만 높이면 지연이 악화될 수 있다

큰 라우터 버퍼는 패킷 손실을 늦추지만 큐 대기 시간을 늘릴 수 있다. 처리량이 높아도 요청 응답 시간이 나빠지는 이유다.

능동 큐 관리(Active Queue Management, AQM)는 큐가 완전히 차기 전에 패킷을 버리거나 ECN으로 혼잡을 알린다.

애플리케이션 재시도가 혼잡을 키울 수 있다

네트워크 오류가 발생했을 때 모든 클라이언트가 즉시 재시도하면 트래픽이 한꺼번에 증가한다. 재시도에는 지수 백오프(Exponential Backoff)와 무작위 지연(Jitter)을 적용해야 한다.

text
1초 대기 → 2초 대기 → 4초 대기 → 8초 대기

운영체제 설정을 무조건 바꾸면 안 된다

혼잡 제어 알고리즘 선택은 RTT, 손실률, 대역폭, 트래픽 형태에 따라 결과가 달라진다. 변경 전후의 처리량, 지연, 재전송률을 같은 조건에서 측정해야 한다.

컨테이너 내부 설정도 호스트 커널과 네트워크 네임스페이스의 영향을 받는다.

8. 핵심 정리

  • 혼잡 제어(Congestion Control)는 네트워크 경로의 과부하를 줄이기 위해 송신량을 조절한다.
  • 실제 전송 가능량은 min(cwnd, rwnd)로 제한된다.
  • 느린 시작은 혼잡 윈도우를 빠르게 늘리고, 혼잡 회피는 증가 속도를 낮춘다.
  • TCP Reno는 AIMD 방식으로 전송량을 늘리고 손실 발생 시 줄인다.
  • 타임아웃은 강한 혼잡 신호이며, 중복 ACK는 빠른 재전송에 사용될 수 있다.
  • CUBIC과 BBR 같은 알고리즘은 서로 다른 방식으로 네트워크 용량을 추정한다.
  • 처리량뿐 아니라 RTT, 큐 대기, 재전송률, 패킷 손실을 함께 측정해야 한다.