dh_0e

[Transport Layer] TCP (Transmission Control Protocol) Part III. 본문

Network Programming/Transport Layer

[Transport Layer] TCP (Transmission Control Protocol) Part III.

dh_0e 2025. 6. 4. 17:14

Congestion Control

Congetsion Window (cwnd)

  • 송신자의 윈도우 크기를 결정하는 요소로 두 가지 요인에 의해 결정됨
    • 수신자가 광고한 윈도우 크기(rwnd)
      • 수신자의 버퍼 공간에 따라 결정됨
    • 네트워크 혼잡 상태(cwnd)
      • 작은 값을 쓰다가 점점 키워감, segment가 loss가 나면 cwnd를 줄여서 설정 
      • 네트워크가 데이터를 처리할 수 있는 속도보다 송신 속도가 빠를 경우, 네트워크가 송신자에게 속도를 줄이라고 알림
  • 실제 송신 윈도우 크기 = min(rwnd, cwnd) / 대부분 rwnd > cwnd

Congestion Policy

Slow Start (느린 시작)

  • 전송 속도를 느리게 시작하지만, treshold에 도달할 때까지 점점 빠르게 증가시킴
  • Exponential Increase (지수 함수 증가)
  • treshold(특정 임계값)에 도달하면, 증가 속도를 줄임(AI)
  • 초기 cwnd는 1 MSS(Maximum Segment Size)로 시작
    • MSS: 연결 수립 시 정해지는 세그먼트 최대 크기
  • 즉, 수신 확인(ACK)이 올 때마다 전송 속도를 지수적으로 증가시킴

rwnd(수신 윈도우)가 cwnd보다 훨씬 크다고 가정 >> 송신 윈도우 크기는 항상 cwnd일 때

 

Congestion Avoidance (혼잡 회피)

  • 혼잡을 미리 방지하기 위한 단계
  • 혼잡이 발생하기 전에 미리 속도를 늦추는 것이 목적
  • Additive Increase(선형 방식)으로 cwnd를 증가시킴
    • 즉, slow start보다 느리게 증가해서 혼잡 가능성을 낮춤

cwnd가 4.5가 될 순 없지만 ACK마다 cwnd는 늘어나고 있음

  • Additive Increase의 동작 시점: cwnd가 Exponential Increase 중에 "Slow Start Treshold"에 도달하면
    • >> Slow Start 중단 / Congestion Avoidance(Additive Increase) 단계 진입
  • 전체 RTT 주기에 대해 cwnd를 1 MSS 만큼만 증가시킴
    • 사실 1 ACK 당 소수점만큼 증가시키는 중

 

Congestion detection (혼잡 감지)

  • 송신자가 세그먼트를 재전송해야 할 때 혼잡이 발생했다고 판단
    • RTO timer가 time-out - 강한 Congestion detection
    • 3개의 중복 ACK 발생 - 약한 Congestion detection

RTO time-out (Strong C.D)

  1. threshold를 현재 cwnd의 절반으로 설정
  2. cwnd를 1 MSS로 초기화
  3. Slow Start 단계로 다시 진입

Triple Duplicate ACK (Weak C.D)

  • 하나의 세그먼트는 손실됐지만, 그 앞의 세그먼트들은 정상적으로 도착, 그 뒤의 세그먼트들 또한 일부 도착
    1. threshold를 현재 cwnd의 절반으로 설정
    2. cwnd를 threshold로(사실 그냥 절반으로 나눔) 설정
    3. Congestion Avoidance 단계로 진입

case I: RTO time-out, case II: triple duplicate ACK

  • cwnd가 8에서 16이 될 때 threshold 10보다 크므로 9, 10 이런 식으로 아닌가?

cwnd>ssthresh일 때, ssthresh = 1/2 window ??? cwnd=1/2 window or cwnd=ssthresh 아닌가

  • 흠 TCP Tahoe, TCP Reno 방식이 있다는데 방식이 다 다른 건가 표준은 fast recovery를 사용해서 threshold + 3으로 설정한다는데 나중에 더 알아보자
 

TCP Congestion Control

혼잡 제어(Congestion Control)은 안정적인 네트워크 구축을 위해 필요한 기능이다. 인터넷을 비롯한 모든 네트워크는 하드웨어적 한계가 존재하므로 감당할 수 있는 용량이 정해져 있다.  이러한

dev-ws.tistory.com

 


TCP timers


Retransmission Timer

  • TCP가 전송 큐의 첫번 째 세그먼트를 보낼 때 타이머 시작
    • RTO 타이머는 연결당 하나 (세그먼트마다 따로 있는 게 아님)
  • 타이머가 만료되면 첫 번째 세그먼트를 재전송하고, 타이머 재시작
  • 하나 이상의 세그먼트가 누적 ACK로 확인되면 해당 세그먼트를 큐에서 제거
  • 근데 첫번째 세그먼트만 ACK가 오고 다음 세그먼트들은 타이머를 안 쓰나 ?!?!
    • 큐가 비면 타이머 정지
    • 남아 있으면 타이머 재시작해서 나머지 세그먼트들도 파악
시각   | 동작 내용                                  | 타이머 상태
-------|---------------------------------------------|-------------------------------
0.0s   | A 전송 (Seq 1~1000)                         | ✅ 타이머 시작 (A 기준)
0.1s   | B 전송 (Seq 1001~2000)                      | 유지 중
0.2s   | C 전송 (Seq 2001~3000)                      | 유지 중
1.0s   | ACK 1001 도착 (A 수신됨)                   | 🔁 타이머 재시작 (B 기준)
1.8s   | ACK 2001 도착 (B 수신됨)                   | 🔁 타이머 재시작 (C 기준)
2.5s   | ACK 3001 도착 (C 수신됨)                   | 🛑 타이머 종료 (모두 확인됨)

RTT(Round-Trip Time)

  • 세그먼트를 보내고, 해당 ACK를 받을 때까지 걸리는 시간
  • RTTsSmoothed RTT
    • 측정된 RTT 값을 기반으로 가중 평균 필터링을 한 값
    • 노이즈를 줄이기 위한 목적
  • RTTmMeasured RTT
    • 실제로 측정된 RTT
  • (Init) RTTs=RTTm
  • (Update) RTTs=(1-a) * RTTs + a * RTTm

  • a과거와 현재의 가중치를 설정하는 값
    • a가 커질수록 현재 비중이 올라감
    • a가 작아질수록 과거 비중이 올라감

 

RTO(Retrensmission Timeout)

  • 이름은 재전송 시간초과지만 본 의미는 "재전송하기 전까지 기다리는 시간 한계"
  • RTT 측정값을 바탕으로 RTO 값을 설정
  • RTTd평균과의 편차값
  • RTO = RTTs + 4 * RTTd

Karn's Algorithm

  • RTT를 구할 때 TCP가 세그먼트를 전송한 뒤, 도착한 ACK가
    원래 보낸 것에 대한 건지, 재전송한 것에 대한 건지 구분이 안 됨
  • >> 재전송된 세그먼트의 RTT는 측정하지 않는다
  • 정확한 RTT 측정이 불가능하므로 RTO 계산에 방해가 되지 않게 함

Exponential Backoff 

  • RTO가 expire 되면 세그먼트를 재전송할 때마다 RTO 값을 2배로 늘림
    • 첫 번째 재전송: 1 x RTO
      두 번째 재전송: 2 x RTO
      세 번째 재전송: 4 x RTO
      네 번째 재전송: 8 x RTO
      ...
  • 재전송 횟수가 많을수록 기다리는 시간도 늘어남

Persistence Timer

수신자가 window size 0을 광고하면 송신자는 nonzero window size를 받을 때까지 데이터 전송을 멈춤
그 이후 수신자가 윈도우를 늘렸는데 ACK가 손실되면? 서로를 기다리는 Deadlock 발생
  • 송신자가 수신자로부터 0 윈도우 광고를 받으면 Persistence Timer 시작
  • 타이머가 만료되면, 송신자는 "Probe" 세그먼트 전송
    • 1바이트의 새로운 데이터만 포함
    • Sequence Number는 있지만 절대 확인되지 않음
  • 수신자가 새로운 윈도우 크기를 다시 광고하게 만들기 위함
  • Probe 세그먼트를 보낸 뒤에도 Persistence timer는 다시 켜지며 주기적으로 Probe를 보냄

Keepalive Timer

  • 오랫동안 아무 통신도 없는 연결을 끊기 위해 사용 (주로 서버에서 활성화)
  • 보통 TLS의 보안 메커니즘 관리에 유저 세션의 timer가 더 짧아서 먼저 끊김
  • 서버가 클라이언트로부터 데이터를 일정 시간 동안 받지 못하면 keepalive timer 동작
  • 일정 시간(일반적으로 2시간)이 지나면 서버는 Keepalive Probe를 보내고 일정 횟수 이상 응답이 없으면 연결 종료

TIME-WAIT Time

  • MSL(Maximum Segment Lifetime): 일반적으로 60초가 기본값
  • 연결 종료 시점에서
    • 마지막 ACK를 재전송할 수 있는 시간 확보
    • 중복된 세그먼트가 네트워크에 남아 있을 경우 대비
  • TIME-WAIT 상태는 2 x MSL 만큼 유지
  • 보통 먼저 FIN을 보낸 쪽에서 발생
  • ex) 클라이언트가 서버에 접속해서 데이터를 주고받다가 ctrl+c로 프로세스를 강제 종료함
          다음에 다시 서버를 같은 포트로 실행하려고 하면 에러 발생
           >> 이전 연결이 아직 TIME-WAIT 상태로 남아 있어서, OS가 해당 포트를 재사용하지 못하게 막고 있기 때문

TCP Options

  • TCP 헤더에는 최대 40 bytes까지 옵션 필드를 추가할 수 있음
  • single-byte options, multiple-byte options로 나뉨

빨간색 부분만 자세히 할 거임


MSS (Maximum Segment Size)

  • 수신 측이 한 번에 수용할 수 있는 최대 데이터 크기를 정하고 handshake 과정에서 알려줌
  • 근데 segment 전체 크기가 아니라 data 부분만 의미
 

[Network Layer] IPv4 Datagrams

IntroductionIP(Internet Protocol)는 TCP/IP 프로토콜 스택의 네트워크 계층에서 사용되는 전송 메커니즘이다.IP는 비신뢰성(unreliable) 및 비연결성(connectionless) 특성을 가진 데이터그램 기반 프로토콜이다.B

dh-0e.tistory.com

  • 정의하지 않으면 Default 값은 536 bytes

Window Scale Factor

  • window size를 증가시키기 위해 사용

  • TCP 헤더의 윈도우 필드가 16 bit면 최대 65,535 bytes까지만 표현이 가능했음
    • 고속 네트워크나 대역폭-지연 곱이 큰 환경의 경우 부족함
  • Window Scaling option 사용 시
    윈도우 크기 = 실제 값(윈도우 필드에 적힌 window size) x $2^{window scale factor}$
    • ex) TCP 헤더 윈도우 필드 값: 16,384
            Window Scale Factor: 3
            → 최종 윈도우 크기 = 16,384 × $2^3$ = 131,072 bytes (128KB)
  • handshake에만 사용 가능하며 이후 데이터 전송 중에는 변경 불가

Timestamp

  • 10 bytes (비교적 큰 편)
  • 연결을 시작할 때(handshake) 클라이언트가 SYN 세그먼트에 timestamp 옵션을 포함
    • O: SYN+ACK에 timestamp 옵션을 포함해서 응답하면 양쪽 모두 timestamp 옵션 사용 가능 
    • X: 옵션을 포함하지 않으면 사용 안 함
  • 정확하고 일관된 RTT 계산을 위한 보조 수단으로 wraparound sequence numbers 방지

Measuring RTT

  • 모든 계산은 송신자(sender)의 시계를 기준으로 함
  • 송신자와 수신자의 시계 동기화는 필요 없음
  • 수신자가 유지하는 변수 2가지
    • lastack: 마지막으로 보낸 ACK의 시퀀스 번호
    • tsrecent: 가장 최근에 받은 timestamp 값 저장용 변수
  • 가장 먼저 수신한 세그먼트가 새로운 데이터일 때(어차피 첫 번째 세그먼트만 RTT 계산)
    >> 그 세그먼트의 timestamp 값을 tsrecent 변수에 저장함
  • 이후 이 값을 활용해 송신 측이 RTT를 정확히 계산 가능

 

SACK-Permitted & SACK Options

SACK-Permitted

  • SACK Permitted Option: 단순히 "나 SACK 쓸 수 있어요" 표시
    • Handshake 중 SYN 단계에서만 허용됨

SACK Permitted(사용 가능 여부)를 알려줄 때, Kind, Length 모두 고정값

SACK Option

  • SACK(Selective Acknowledgment): 손실되지 않은 데이터 범위를 명시적으로 알려서 송신자가 정확히 손실된 부분만 재전송할 수 있도록 돕는 기능
  • 데이터 전송 중에만 사용 (Handshake 중에 SACK-Permitted 옵션을 서로 교환한 이후)
  • SACK Option은 수신자가 받은 블록들의 범위를 리스트로 포함
  • 송신자는 이를 보고 정확히 손실된 구간만 재전송 가능
  • 데이터 전송 중 들어가는 SACK은 Kind: 5, Length: 2 + (8 x 블록 수)
    • 블록 수(한 쌍)는 최대 4개까지 (각 블록이 8 bytes = 4 bytes 시작 + 4bytes 끝 바이트 번호)

SACK Example

  • 각 4 bytes인 블록 2개가 한 쌍을 이뤄 시작과 끝 바이트 번호를 나타냄
    • 사실 끝 바이트 번호실제로 받은 byte + 1 한 값
    • 끝 번호 ex) 위 예시의 6001은 exclusive(미포함)
    • 즉, 4001 ~ 6000까지 바이트가 정상 수신됨을 의미 >> 시퀀스 번호 표현 방식
  • 위 예시에 시작과 끝을 나타내는 블록 수가 2개이므로 length = 2 + (8 bytes * 2) = 18, kind는 SACK이기 때문에 5 고정