TCP 흐름 제어와 혼잡 제어 (TCP Flow Control and Congestion Control)
한눈에 보기
흐름 제어는 수신자의 버퍼를, 혼잡 제어는 네트워크를 보호하는 TCP의 속도 조절 기능이다. 슬라이딩 윈도우, 슬로 스타트와 AIMD, CUBIC과 BBR까지 정리한다.
최종 업데이트 2026.09.17
TCP 흐름 제어(flow control)는 수신자가 처리할 수 있는 만큼만 보내도록 송신 속도를 조절하는 기능이고, 혼잡 제어(congestion control)는 네트워크 경로가 감당할 수 있는 만큼만 보내도록 조절하는 기능이다. 흐름 제어는 수신자의 버퍼를 보호하고 혼잡 제어는 네트워크 전체를 보호한다는 점에서 목적이 다르다. TCP 송신자는 두 제약 중 더 작은 값을 한도로 삼아 전송량을 결정한다.
왜 두 가지 제어가 모두 필요한가
수신 애플리케이션이 데이터를 느리게 소비하면 수신 버퍼가 넘쳐 도착한 데이터가 버려진다. 이것을 막는 것이 흐름 제어다. 한편 수신자는 여유가 있어도 중간의 라우터 큐가 가득 차면 패킷이 손실된다. 모든 송신자가 손실을 무시하고 계속 밀어 넣으면 재전송이 재전송을 부르는 혼잡 붕괴(congestion collapse)가 일어나는데, 실제로 1980년대 인터넷이 이를 겪은 뒤 혼잡 제어가 TCP에 도입되었다. 요약하면 흐름 제어의 병목은 종단의 수신자이고, 혼잡 제어의 병목은 경로 중간의 네트워크라는 점을 먼저 구분해야 한다.
흐름 제어: 슬라이딩 윈도우
세그먼트 하나를 보내고 ACK를 기다리는 정지-대기 방식은 왕복 시간 동안 회선이 놀게 되어 처리량이 왕복 시간에 반비례해 떨어진다. 그래서 TCP는 확인 응답을 기다리지 않고 일정량을 연속 전송하는 슬라이딩 윈도우(sliding window) 방식을 쓴다. 수신자는 ACK를 보낼 때마다 자신의 버퍼 여유 공간을 수신 윈도우(rwnd, receive window) 필드에 실어 알려 주고, 송신자는 확인받지 못한 데이터의 양이 이 값을 넘지 않도록 한다. ACK가 도착해 윈도우가 앞으로 미끄러지면 그만큼 새 데이터를 보낼 수 있다.
수신 버퍼가 가득 차면 rwnd가 0이 되어 송신이 멈춘다. 이후 여유가 생겼다는 윈도우 갱신 통지가 유실되면 양쪽이 영원히 기다리는 교착이 생길 수 있으므로, 송신자는 작은 탐침 패킷을 주기적으로 보내 윈도우 상태를 확인한다. 원래 윈도우 필드는 16비트라 64KB가 상한이지만, 고대역 장거리 회선을 위해 윈도우 스케일(window scale) 옵션으로 기가바이트급까지 확장한다.
혼잡 제어: 네 가지 기본 동작
송신자는 네트워크 상태를 직접 알 수 없으므로, 혼잡 윈도우(cwnd, congestion window)라는 자체 추정치를 유지하며 손실과 지연 신호로 조절한다. 고전적인 절차는 다음과 같다.
- 슬로 스타트(slow start): 작은 cwnd로 시작해 ACK를 받을 때마다 늘려서 왕복마다 대략 두 배로 키운다. 이름과 달리 증가 속도는 지수적이다.
- 혼잡 회피(congestion avoidance): cwnd가 임계값(ssthresh)에 도달하면 왕복당 1세그먼트씩 선형으로 늘리며 조심스럽게 한계를 탐색한다.
- 빠른 재전송(fast retransmit): 같은 번호의 중복 ACK가 3번 도착하면 타임아웃을 기다리지 않고 해당 세그먼트를 즉시 재전송한다.
- 빠른 회복(fast recovery): 중복 ACK로 감지한 손실은 가벼운 혼잡으로 보고 cwnd를 절반으로 줄여 혼잡 회피부터 다시 진행한다. 반면 타임아웃은 심각한 혼잡으로 보고 cwnd를 1로 되돌려 슬로 스타트부터 다시 시작한다.
이렇게 선형으로 늘리고 손실 시 절반으로 줄이는 패턴을 AIMD(Additive Increase Multiplicative Decrease)라고 하며, 여러 흐름이 대역폭을 공평하게 나누어 갖도록 수렴시키는 성질이 있다.
현대의 혼잡 제어 알고리즘
손실 기반 고전 알고리즘(Reno 계열)은 대역폭이 크고 지연이 긴 회선에서 회복이 느리다는 한계가 있다. 리눅스 기본값인 CUBIC은 감소 이후 3차 함수 곡선으로 빠르게 회복해 고속 회선 활용도를 높였다. 구글이 설계한 BBR(Bottleneck Bandwidth and Round-trip propagation time)은 손실을 기다리는 대신 병목 대역폭과 최소 왕복 시간을 직접 측정해 전송률을 정하므로, 라우터 큐를 가득 채워 지연을 키우는 버퍼블로트(bufferbloat) 문제를 완화한다. 또한 패킷을 버리기 전에 혼잡 조짐을 표시해 알리는 ECN(Explicit Congestion Notification)도 손실 없이 혼잡을 전달하는 보조 수단으로 쓰인다. QUIC도 같은 혼잡 제어 원리를 사용자 공간에서 구현해 알고리즘 교체가 더 쉽다. 결국 어떤 알고리즘이든 "네트워크 상태를 어떤 신호로 추정하고, 얼마나 공격적으로 대역폭을 차지할 것인가"라는 같은 질문에 대한 서로 다른 답이라고 이해하면 된다.
흐름 제어와 혼잡 제어 비교
| 구분 | 흐름 제어 | 혼잡 제어 |
|---|---|---|
| 보호 대상 | 수신자의 버퍼 | 네트워크 경로(라우터 큐) |
| 제어 주체 | 수신자가 rwnd로 알림 | 송신자가 cwnd를 추정 |
| 정보의 출처 | ACK에 실린 명시적 값 | 손실, 지연, ECN 등 간접 신호 |
| 핵심 기법 | 슬라이딩 윈도우, 윈도우 스케일 | 슬로 스타트, AIMD, CUBIC, BBR |
| 실제 송신 한도 | min(rwnd, cwnd) | min(rwnd, cwnd) |
면접에서 이렇게 물어본다
- 흐름 제어와 혼잡 제어의 차이를 보호 대상과 제어 주체 관점에서 설명해 보라.
- 슬라이딩 윈도우가 정지-대기 방식보다 처리량이 높은 이유는 무엇인가.
- 슬로 스타트와 혼잡 회피는 각각 어떤 방식으로 윈도우를 키우고 언제 전환되는가.
- 중복 ACK 3개로 감지한 손실과 타임아웃으로 감지한 손실은 왜 다르게 처리하는가.
- CUBIC과 BBR은 기존 방식의 어떤 한계를 어떻게 개선했는가.
네트워크에서 같이 읽기
- I/O 모델: 블로킹, 논블로킹, I/O 멀티플렉싱 (I/O Models)I/O 모델은 애플리케이션이 커널에 입출력을 요청했을 때 결과를 기다리는 방식이다. 블로킹, 논블로킹, I/O 멀티플렉싱의 동작 원리와 select, epoll, io_uring, 이벤트 루프까지 정리한다.
- CORS (Cross-Origin Resource Sharing)CORS는 브라우저의 동일 출처 정책을 안전하게 완화하는 HTTP 헤더 기반 메커니즘이다. 단순 요청과 프리플라이트, 자격 증명, 서버 설정에서 자주 하는 실수까지 정리했다.
- DNS (Domain Name System)DNS는 도메인 이름을 IP 주소로 변환하는 분산 계층형 이름 해석 시스템이다. 루트, TLD, 권한 서버와 재귀 리졸버의 역할, 해석 과정, 레코드 타입, 캐싱과 DNSSEC, DoH까지 정리한다.
- HTTP와 HTTPS (HTTP and HTTPS)HTTP는 웹의 요청과 응답을 주고받는 프로토콜이고, HTTPS는 이를 TLS 위에 얹어 암호화한 것이다. 무상태성, 인증서 신뢰 사슬, HTTP/1.1과 2, 3의 차이까지 비교해 정리한다.
이 개념, 내 코드에서는 어떻게 물어볼까요
데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.
공고 넣고 시작하기