- Authors

- Name
- Youngju Kim
- @fjvbn20031
본 포스팅은 James Kurose, Keith Ross의 Computer Networking: A Top-Down Approach (6th Edition) 교재를 기반으로 정리한 내용입니다.
- 1. TCP 연결
- 2. TCP 세그먼트 구조
- 3. 순서 번호와 확인 번호
- 4. RTT 추정과 타임아웃
- 5. TCP 신뢰적 데이터 전송
- 6. 흐름 제어 (Flow Control)
- 7. TCP 혼잡 제어 (Congestion Control)
- 8. TCP 공정성 (Fairness)
- 9. 정리
- 10. 확인 문제
1. TCP 연결
1.1 TCP 연결의 특징
TCP 연결의 핵심 속성:
├── 연결 지향 (Connection-Oriented)
│ 데이터 전송 전에 반드시 연결 수립
├── 전이중 (Full-Duplex)
│ 양방향 동시 데이터 전송
├── 점대점 (Point-to-Point)
│ 정확히 하나의 송신자와 하나의 수신자
└── 바이트 스트림 서비스
메시지 경계 없이 연속된 바이트 흐름
1.2 3-Way Handshake
TCP 연결 수립 과정이다.
클라이언트 서버
| |
|── SYN (seq=client_isn) ──────>| 1단계: SYN
| |
|<── SYN+ACK ──────────────────| 2단계: SYN+ACK
| (seq=server_isn, |
| ack=client_isn+1) |
| |
|── ACK (ack=server_isn+1) ───>| 3단계: ACK
| (데이터 포함 가능) |
| |
각 단계 설명
1단계 - SYN:
- 클라이언트가 SYN 세그먼트 전송
- SYN 비트 = 1
- 초기 순서 번호(client_isn) 설정
- 데이터 없음
2단계 - SYN+ACK:
- 서버가 SYN+ACK 세그먼트 응답
- SYN 비트 = 1, ACK 비트 = 1
- 서버의 초기 순서 번호(server_isn) 설정
- 확인 번호 = client_isn + 1
3단계 - ACK:
- 클라이언트가 ACK 세그먼트 전송
- SYN 비트 = 0 (연결 수립 완료)
- 이 세그먼트부터 데이터 포함 가능
1.3 TCP 연결 종료 (4-Way Handshake)
클라이언트 서버
| |
|── FIN ───────────────────────>| 1단계
| |
|<── ACK ──────────────────────| 2단계
| |
|<── FIN ──────────────────────| 3단계
| |
|── ACK ───────────────────────>| 4단계
| |
| (TIME_WAIT: 30초 대기) |
| → 연결 완전 종료 |
TIME_WAIT 상태: 마지막 ACK가 손실되었을 때 FIN 재전송에 대응하기 위해 일정 시간 대기한다.
2. TCP 세그먼트 구조
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
┌───────────────────────┬───────────────────────┐
│ 출발지 포트 (16) │ 목적지 포트 (16) │
├───────────────────────┴───────────────────────┤
│ 순서 번호 (32) │
├───────────────────────────────────────────────┤
│ 확인 번호 (32) │
├────────┬──────┬───────┬───────────────────────┤
│헤더길이 │미사용│플래그 │ 수신 윈도우 (16) │
│ (4) │ (6) │(6bits)│ │
├────────┴──────┴───────┼───────────────────────┤
│ 체크섬 (16) │ 긴급 포인터 (16) │
├───────────────────────┴───────────────────────┤
│ 옵션 (가변 길이) │
├───────────────────────────────────────────────┤
│ │
│ 데이터 (가변 길이) │
│ │
└───────────────────────────────────────────────┘
주요 필드
| 필드 | 크기 | 설명 |
|---|---|---|
| 순서 번호 | 32 bits | 세그먼트 데이터의 첫 바이트 번호 |
| 확인 번호 | 32 bits | 기대하는 다음 바이트 번호 |
| 수신 윈도우 | 16 bits | 수신 가능한 바이트 수 (흐름 제어) |
| 헤더 길이 | 4 bits | TCP 헤더 길이 (4바이트 단위) |
| 플래그 비트 | 6 bits | SYN, ACK, FIN, RST, PSH, URG |
3. 순서 번호와 확인 번호
3.1 순서 번호 (Sequence Number)
세그먼트에 포함된 데이터의 첫 번째 바이트의 바이트 스트림 번호다.
예시: 500,000 바이트 파일, MSS = 1,000 바이트
세그먼트 1: seq = 0, 데이터 = 바이트 0~999
세그먼트 2: seq = 1000, 데이터 = 바이트 1000~1999
세그먼트 3: seq = 2000, 데이터 = 바이트 2000~2999
...
세그먼트 500: seq = 499000, 데이터 = 바이트 499000~499999
3.2 확인 번호 (Acknowledgment Number)
수신자가 다음으로 기대하는 바이트 번호다. 누적 확인(cumulative acknowledgment) 방식이다.
예시: 텔넷 세션에서 'C'를 입력
호스트 A 호스트 B
|── seq=42, ack=79, 'C' ───>|
| | 'C' 수신, 에코 응답
|<── seq=79, ack=43, 'C' ───|
| |
|── seq=43, ack=80 ──────── >| (ACK)
seq=42: A가 보내는 데이터의 42번째 바이트
ack=79: A가 B로부터 79번째 바이트를 기대
ack=43: B가 A로부터 43번째 바이트를 기대 (42번 수신 완료)
4. RTT 추정과 타임아웃
4.1 SampleRTT
실제 측정한 RTT 값이다. 세그먼트 전송 시점부터 ACK 수신 시점까지의 시간이다.
- 재전송된 세그먼트는 측정에서 제외
- SampleRTT는 변동이 크다
4.2 EstimatedRTT (지수 가중 이동 평균)
EstimatedRTT = (1 - alpha) * EstimatedRTT + alpha * SampleRTT
alpha = 0.125 (권장값)
→ 최근 측정값에 더 큰 가중치
→ SampleRTT의 급격한 변동을 완화
4.3 DevRTT (RTT 편차)
DevRTT = (1 - beta) * DevRTT + beta * |SampleRTT - EstimatedRTT|
beta = 0.25 (권장값)
4.4 타임아웃 간격
TimeoutInterval = EstimatedRTT + 4 * DevRTT
EstimatedRTT: 평균 RTT 추정
4 * DevRTT: 안전 마진 (변동성 고려)
RTT 추정 예시 (시간 경과에 따른 변화):
RTT
(ms)
^
│ * *
│ * * * * SampleRTT (변동 큼)
│ ** * * *
│ ___________*____ EstimatedRTT (안정적)
│ TimeoutInterval (상한선)
└──────────────────> 시간
5. TCP 신뢰적 데이터 전송
TCP는 IP의 비신뢰적 서비스 위에 신뢰적 전송을 구현한다.
5.1 TCP 송신자의 핵심 이벤트
이벤트 1: 상위 계층으로부터 데이터 수신
→ 세그먼트 생성, 순서 번호 부여
→ IP에 전달
→ 타이머가 실행 중이 아니면 시작
이벤트 2: 타이머 만료
→ 가장 오래된 미확인 세그먼트 재전송
→ 타이머 재시작
이벤트 3: ACK 수신
→ SendBase 업데이트 (누적 확인)
→ 아직 미확인 세그먼트가 있으면 타이머 재시작
5.2 TCP 재전송 시나리오
시나리오 1: ACK 손실
호스트 A 호스트 B
|── seq=92, 8바이트 ───>|
| |── ACK=100 (손실!)
| 타이머 만료 |
|── seq=92, 8바이트 ───>| (재전송)
|<── ACK=100 ───────── |
시나리오 2: 조기 타임아웃
호스트 A 호스트 B
|── seq=92, 8바이트 ───>|
|── seq=100, 20바이트 ─>|
| |── ACK=100
| 타이머 만료! |── ACK=120
| (ACK=100이 아직 도착 안 함)
|── seq=92, 8바이트 ───>| (불필요한 재전송)
|<── ACK=100 ───────── |
|<── ACK=120 ───────── | (누적 ACK로 자연 해결)
시나리오 3: 누적 ACK
호스트 A 호스트 B
|── seq=92, 8바이트 ───>|
|── seq=100, 20바이트 ─>|
| |── ACK=100 (손실!)
| |── ACK=120 (도착)
|<── ACK=120 ───────── |
ACK=120은 120 이전의 모든 바이트 확인
→ seq=92 패킷도 확인된 것!
→ 재전송 불필요
5.3 빠른 재전송 (Fast Retransmit)
타임아웃을 기다리지 않고, 3개의 중복 ACK를 받으면 즉시 재전송한다.
호스트 A 호스트 B
|── seq=92 ────────────>| ACK=100
|── seq=100 ───────────>| (손실!)
|── seq=120 ───────────>| ACK=100 (중복 1)
|── seq=140 ───────────>| ACK=100 (중복 2)
|── seq=160 ───────────>| ACK=100 (중복 3)
| |
| 3개 중복 ACK 수신! |
| 타이머 만료 전에 즉시 재전송
|── seq=100 ───────────>| ACK=180 (누적 확인)
3개의 중복 ACK = 원래 ACK + 추가 3개 = 총 4번의 동일한 ACK
6. 흐름 제어 (Flow Control)
6.1 문제
송신자가 너무 빠르게 데이터를 보내면 수신자의 버퍼가 넘칠 수 있다.
수신 측 버퍼:
┌──────────────────────────────┐
│[데이터][데이터][ 빈 공간 ]│
└──────────────────────────────┘
│←── 사용 중 ──→│←── rwnd ────→│
수신 윈도우
6.2 수신 윈도우 (Receive Window)
TCP는 수신 윈도우(rwnd) 를 통해 흐름 제어를 수행한다.
rwnd = RcvBuffer - (LastByteRcvd - LastByteRead)
RcvBuffer: 수신 버퍼의 전체 크기
LastByteRcvd: 마지막으로 수신된 바이트 번호
LastByteRead: 상위 계층이 읽어간 마지막 바이트 번호
6.3 동작 방식
수신자: ACK에 rwnd 값을 포함하여 전송
ACK 세그먼트:
┌────────┬────────┐
│ ACK=N │ rwnd=X │ "N번째 바이트까지 받았고,
└────────┴────────┘ X 바이트만큼 더 받을 수 있어"
송신자: rwnd를 초과하지 않도록 전송량 제한
LastByteSent - LastByteAcked <= rwnd
rwnd가 줄어들면 → 전송 속도 감소
rwnd가 0이면 → 전송 중단 (단, 1바이트 프로브 패킷 전송)
rwnd = 0 일 때의 문제
문제: 수신자가 rwnd=0을 보낸 후 버퍼를 비워도,
송신자에게 알릴 방법이 없음 (보낼 데이터가 없으므로)
해결: 송신자가 주기적으로 1바이트 프로브(probe) 세그먼트 전송
→ 수신자가 현재 rwnd 값을 포함한 ACK로 응답
→ 빈 공간이 생기면 송신 재개
7. TCP 혼잡 제어 (Congestion Control)
7.1 혼잡이란
혼잡(Congestion):
네트워크 내 데이터 양이 네트워크 용량을 초과하는 상태
증상:
├── 패킷 손실 (라우터 버퍼 오버플로)
├── 긴 지연 (라우터 큐에서 대기)
└── 불필요한 재전송 (타임아웃으로 인한)
7.2 혼잡 제어 vs 흐름 제어
흐름 제어: 수신자 보호 (수신 버퍼 오버플로 방지)
→ rwnd로 제어
혼잡 제어: 네트워크 보호 (네트워크 과부하 방지)
→ cwnd(congestion window)로 제어
실제 전송량:
LastByteSent - LastByteAcked <= min(cwnd, rwnd)
7.3 AIMD (Additive Increase Multiplicative Decrease)
TCP 혼잡 제어의 기본 철학이다.
cwnd 조정 원칙:
패킷 손실 없음 (ACK 정상 수신):
→ cwnd 증가 (대역폭 탐색)
패킷 손실 발생 (타임아웃 또는 3중복 ACK):
→ cwnd 감소 (혼잡 완화)
"톱니 형태" 패턴:
cwnd
^
│ /\ /\ /\
│ / \ / \ / \
│ / \ / \ / \
│ / \/ \/ \
└──────────────────────────> 시간
7.4 슬로 스타트 (Slow Start)
연결 초기에 cwnd를 지수적으로 증가시킨다.
초기 cwnd = 1 MSS
각 ACK 수신 시: cwnd += 1 MSS
(한 RTT에 모든 ACK를 받으면 cwnd가 2배)
cwnd 변화:
RTT 1: cwnd = 1 MSS → 1개 세그먼트 전송
RTT 2: cwnd = 2 MSS → 2개 세그먼트 전송
RTT 3: cwnd = 4 MSS → 4개 세그먼트 전송
RTT 4: cwnd = 8 MSS → 8개 세그먼트 전송
...
지수적 증가: 1, 2, 4, 8, 16, 32, ...
슬로 스타트의 종료 조건
1. cwnd >= ssthresh (느린 시작 임계값) → 혼잡 회피로 전환
2. 타임아웃 발생 → ssthresh = cwnd/2, cwnd = 1 MSS, 슬로 스타트 재시작
3. 3개 중복 ACK → ssthresh = cwnd/2, cwnd = ssthresh + 3, 빠른 회복
7.5 혼잡 회피 (Congestion Avoidance)
cwnd가 ssthresh에 도달한 후 선형적으로 증가시킨다.
각 RTT마다: cwnd += 1 MSS
(ACK 하나당: cwnd += MSS * MSS / cwnd)
cwnd 변화 (MSS 단위):
RTT n: cwnd = 10
RTT n+1: cwnd = 11
RTT n+2: cwnd = 12
...
선형 증가: 10, 11, 12, 13, ...
혼잡 이벤트 시 동작
타임아웃 발생:
ssthresh = cwnd / 2
cwnd = 1 MSS
→ 슬로 스타트로 복귀
3개 중복 ACK:
ssthresh = cwnd / 2
cwnd = ssthresh + 3 MSS
→ 빠른 회복으로 전환
7.6 빠른 회복 (Fast Recovery)
3개의 중복 ACK를 받았을 때의 상태다. TCP Reno에서 도입되었다.
빠른 회복 동작:
1. 중복 ACK 수신할 때마다: cwnd += 1 MSS
2. 새 ACK 수신 (손실 복구 완료):
cwnd = ssthresh
→ 혼잡 회피로 전환
3. 타임아웃 발생:
ssthresh = cwnd / 2
cwnd = 1 MSS
→ 슬로 스타트로 복귀
7.7 TCP 혼잡 제어 전체 상태 다이어그램
┌─────────────┐
시작 ───────>│ 슬로 스타트 │
│ cwnd 지수 증가│
└──────┬──────┘
│
cwnd >= ssthresh
│
┌──────▼──────┐
┌────>│ 혼잡 회피 │<────┐
│ │ cwnd 선형 증가│ │
│ └──────┬──────┘ │
│ │ │
새 ACK 3중복 ACK 타임아웃
(복구완료) │ │
│ ┌──────▼──────┐ │
│ │ 빠른 회복 │ │
└─────│ cwnd 조정 │ │
└─────────────┘ │
│ │
타임아웃 │
└────────────┘
ssthresh=cwnd/2
cwnd=1 MSS
→ 슬로 스타트
7.8 TCP Tahoe vs TCP Reno
cwnd
^
│ *
│ * *
│ * * TCP Reno
│ * * /
│ * * *
│ * * * *
│ * ** *
│ * *
│ *
│ * TCP Tahoe: 항상 cwnd=1로 복귀
│ *
└─────────────────────────────> 시간
↑ ↑
손실1 손실2
| 이벤트 | TCP Tahoe | TCP Reno |
|---|---|---|
| 타임아웃 | cwnd = 1 MSS | cwnd = 1 MSS |
| 3중복 ACK | cwnd = 1 MSS | cwnd = cwnd/2 (빠른 회복) |
8. TCP 공정성 (Fairness)
8.1 공정성의 정의
용량 R인 병목 링크를 K개의 TCP 연결이 공유할 때, 각 연결이 R/K의 처리량을 얻는 것이 이상적이다.
8.2 TCP가 공정한 이유
두 연결이 대역폭 R을 공유하는 경우:
처리량2
^
│ ╲ 공정 지점
│ ╲ (R/2, R/2)
│ ╲ *
R │ ╲ / \
│ * *
│ / ╲ \
│ / * *
│ / / ╲ \
│ / / * * → 공정 지점으로 수렴
└──────────────────> 처리량1
0 R
AIMD가 공정 지점으로 수렴하는 과정:
1. 두 연결이 균등하게 cwnd 증가 (Additive Increase)
→ 45도 방향으로 이동 (총합 = R 선을 향해)
2. 손실 발생 시 각각 cwnd를 절반으로 (Multiplicative Decrease)
→ 원점 방향으로 이동
3. 반복하면 공정 지점(R/2, R/2)으로 수렴
8.3 공정성의 현실적 한계
UDP는 혼잡 제어를 하지 않음:
→ TCP가 양보한 대역폭을 UDP가 차지
→ TCP에 불공정
병렬 TCP 연결:
→ 하나의 애플리케이션이 여러 TCP 연결을 열면
→ 단일 연결 애플리케이션보다 더 많은 대역폭 차지
→ 예: 브라우저가 동시에 여러 연결로 웹 객체 다운로드
9. 정리
TCP 핵심 메커니즘 요약:
연결 관리:
├── 3-way handshake (SYN → SYN+ACK → ACK)
└── 4-way handshake 종료 (FIN → ACK → FIN → ACK)
신뢰적 전송:
├── 순서 번호 + 확인 번호 (누적 ACK)
├── 타이머 기반 재전송
├── 빠른 재전송 (3중복 ACK)
└── RTT 추정 (EWMA)
흐름 제어:
└── rwnd (수신 윈도우)로 수신자 보호
혼잡 제어:
├── 슬로 스타트: 지수적 증가
├── 혼잡 회피: 선형적 증가
├── 빠른 회복: 3중복 ACK 시 cwnd 절반
└── AIMD → 공정한 대역폭 공유
10. 확인 문제
Q1. 3-way handshake에서 왜 3번의 메시지 교환이 필요한가?
1번 (SYN): 클라이언트가 연결을 요청하고 자신의 초기 순서 번호를 알린다. 2번 (SYN+ACK): 서버가 연결을 수락하고 자신의 초기 순서 번호를 알리며, 클라이언트의 순서 번호를 확인한다. 3번 (ACK): 클라이언트가 서버의 순서 번호를 확인한다.
이 과정을 통해 양측 모두 상대방의 초기 순서 번호를 확인하고, 연결 의사를 확인한다. 2번만으로는 서버가 보낸 SYN+ACK를 클라이언트가 수신했는지 서버가 알 수 없다.
Q2. 흐름 제어와 혼잡 제어의 차이점은?
- 흐름 제어: 수신자를 보호한다. 수신 버퍼가 넘치지 않도록
rwnd(수신 윈도우)를 사용하여 송신자의 전송량을 제한한다. - 혼잡 제어: 네트워크를 보호한다. 네트워크가 과부하되지 않도록
cwnd(혼잡 윈도우)를 사용하여 송신자의 전송량을 제한한다.
실제 전송량은 min(cwnd, rwnd)에 의해 결정된다.
Q3. 슬로 스타트에서 cwnd가 지수적으로 증가하는데 왜 "느린(slow)" 시작인가?
"느린 시작"이라는 이름은 초기 cwnd가 1 MSS로 매우 작게 시작한다는 데서 유래한다. TCP 이전의 프로토콜들은 연결 시작 시 허용된 최대 윈도우 크기로 바로 데이터를 쏟아부었는데, 이는 네트워크 혼잡을 유발했다. 슬로 스타트는 이에 비해 "느리게" 1 MSS부터 시작한다는 의미다. 물론 지수적 증가이므로 실제로는 빠르게 증가한다.
Q4. TCP Reno에서 타임아웃과 3중복 ACK를 다르게 처리하는 이유는?
3중복 ACK는 특정 세그먼트만 손실되었지만 그 이후의 세그먼트는 도착했다는 의미다. 네트워크가 여전히 일부 패킷을 전달하고 있으므로 혼잡이 심각하지 않다고 판단하여 cwnd를 절반으로만 줄인다 (빠른 회복).
반면 타임아웃은 아무 ACK도 오지 않는 상황으로, 네트워크 혼잡이 심각하다고 판단하여 cwnd를 1 MSS로 초기화하고 슬로 스타트부터 다시 시작한다.