[Daily morning study] TCP 흐름 제어(Flow Control)와 슬라이딩 윈도우 프로토콜

#daily morning study

Image


흐름 제어란

TCP는 신뢰성 있는 데이터 전송을 보장하는데, 이를 위해 두 가지 속도 제어 메커니즘을 사용한다.

  • 흐름 제어(Flow Control): 수신자의 처리 능력에 맞게 송신 속도를 조절
  • 혼잡 제어(Congestion Control): 네트워크 전체의 혼잡 상태에 맞게 속도를 조절

혼잡 제어가 네트워크 관점이라면, 흐름 제어는 송신자 ↔ 수신자 사이의 end-to-end 속도 조절이다. 수신자가 처리하지 못한 데이터가 버퍼에 쌓여 오버플로우가 발생하는 상황을 막는 게 핵심 목적이다.


수신 버퍼와 rwnd

수신자는 수신 버퍼(receive buffer)를 가지고 있고, 애플리케이션이 데이터를 읽어가는 속도보다 패킷이 더 빠르게 들어오면 버퍼가 꽉 찬다.

TCP는 이를 해결하기 위해 수신 윈도우(rwnd, receiver window) 값을 ACK 패킷의 헤더에 담아 송신자에게 전달한다.

TCP 헤더 구조 (일부)
+------------------+------------------+
|   Source Port    | Destination Port |
+------------------+------------------+
|         Sequence Number             |
+-------------------------------------+
|        Acknowledgment Number        |
+------------------+------------------+
| Offset | Flags  |   Window Size     |  ← rwnd
+------------------+------------------+

송신자는 ACK를 받을 때마다 rwnd 값을 확인하고, 이 값을 초과하지 않는 범위에서 데이터를 전송한다.

송신 가능한 데이터 양 = min(cwnd, rwnd)

  • cwnd: 혼잡 제어 윈도우
  • rwnd: 흐름 제어 윈도우

슬라이딩 윈도우 프로토콜

초기에는 데이터를 하나 보내고 ACK를 받으면 다음 데이터를 보내는 Stop-and-Wait 방식이 있었다. 간단하지만 왕복 시간(RTT)만큼 대기해야 하므로 효율이 매우 낮다.

Stop-and-Wait:
송신자:  [data1] ------>
                          수신자: [ACK1] ------>
송신자:  [data2] ------>
                          수신자: [ACK2] ------>

슬라이딩 윈도우는 ACK를 기다리지 않고 윈도우 크기만큼 연속으로 데이터를 전송할 수 있게 한다.

윈도우 크기 = 4인 경우:

송신자: [1][2][3][4] ------>   (동시에 4개 전송)
                          수신자: [ACK2] ------>   (1,2 수신 확인)
송신자:         [3][4][5][6] ------>   (윈도우 슬라이드)

윈도우가 오른쪽으로 이동(슬라이드)하며 전송 가능한 범위가 갱신되는 것이 핵심이다.


윈도우 상태 분류

TCP 송신자 측에서 데이터는 아래 4가지 상태 중 하나다.

|---- 전송 완료 + ACK 받음 ----|---- 전송 완료 + ACK 대기 ----|---- 전송 가능 ----|---- 전송 불가 ----|

     Already ACKed             Sent, Not ACKed              Can Send             Cannot Send
                                 (in-flight)               (in window)
                        |<----------------- 윈도우 범위 ----------------->|
  • Already ACKed: 수신자가 이미 확인한 데이터 — 버퍼에서 제거 가능
  • Sent, Not ACKed: 전송했지만 ACK 대기 중인 데이터 (in-flight)
  • Can Send: 윈도우 내에 있어 즉시 전송 가능한 데이터
  • Cannot Send: rwnd를 초과하여 현재 전송 불가한 데이터

Zero Window와 Window Probe

수신자의 버퍼가 꽉 찼을 때 rwnd = 0을 전송한다. 이 상태를 Zero Window라고 한다.

문제는 rwnd = 0을 받은 송신자가 전송을 멈추고, 수신자가 버퍼를 비워도 이를 알릴 방법이 없다는 것이다. 수신자가 rwnd > 0인 ACK를 보내지 않으면 데드락 상태가 된다.

이를 해결하기 위해 TCP는 Window Probe 패킷을 사용한다.

수신자: rwnd=0 → 송신자: 전송 중단
(수신자 버퍼 비워짐)
송신자: Window Probe 전송 (작은 패킷으로 rwnd 재확인 요청)
수신자: rwnd=8192 (버퍼 여유 생김) → 송신자: 전송 재개

Window Probe는 일정 간격으로 계속 전송되며, Persist Timer에 의해 관리된다.


Silly Window Syndrome

슬라이딩 윈도우에서 발생할 수 있는 비효율적인 동작이다.

수신 측 SWS: 수신 버퍼가 1바이트만 비워졌는데 rwnd=1을 즉시 알리는 경우. 송신자는 1바이트만 보내고, 이를 위해 40바이트 TCP/IP 헤더가 붙어 오버헤드가 커진다.

송신 측 SWS: 애플리케이션이 1바이트씩만 데이터를 쓰는데, TCP가 1바이트 패킷을 즉시 보내는 경우.

해결책

문제해결책설명
수신 측 SWSClark 해법버퍼 여유가 MSS 또는 버퍼 절반 이상일 때만 rwnd 갱신
송신 측 SWSNagle 알고리즘ACK를 받기 전까지 소규모 데이터를 버퍼링해 모아서 전송

Nagle 알고리즘 동작:

if 전송할 데이터 >= MSS:
    즉시 전송
elif 미확인 패킷 없음:
    즉시 전송
else:
    버퍼에 쌓아두고 ACK 받을  전송

단, 실시간 애플리케이션(게임, SSH 등)은 지연을 허용하지 않으므로 TCP_NODELAY 옵션으로 Nagle 알고리즘을 비활성화한다.


흐름 제어 vs 혼잡 제어

구분흐름 제어혼잡 제어
목적수신자 버퍼 오버플로우 방지네트워크 혼잡 방지
제어 기준수신자의 rwnd네트워크 상태(패킷 손실 등)
신호ACK 헤더의 Window Size패킷 손실, ACK 지연
윈도우rwndcwnd
알고리즘슬라이딩 윈도우Slow Start, AIMD, CUBIC 등

실제 전송 가능한 데이터 양은 두 윈도우 중 작은 값으로 결정된다.

effective window = min(rwnd, cwnd)

실제 동작 예시

초기 상태:
- 수신 버퍼: 65535 bytes
- rwnd: 65535

데이터 전송 시작:
- 송신자: 16384 bytes 전송
- 수신자: rwnd = 65535 - 16384 = 49151 → ACK에 포함

수신자 처리 완료:
- 애플리케이션이 8192 bytes 읽음
- 수신자: rwnd = 49151 + 8192 = 57343 → 다음 ACK에 포함

버퍼 포화:
- rwnd = 0 전송 → 송신자 일시 정지
- Window Probe로 상태 재확인 후 전송 재개

정리

  • 흐름 제어는 수신자 버퍼 용량에 맞게 송신 속도를 조절하는 메커니즘이다.
  • 슬라이딩 윈도우를 통해 ACK를 기다리지 않고 연속으로 데이터를 전송할 수 있다.
  • rwnd가 0이 되면 Window Probe로 수신자 상태를 주기적으로 확인한다.
  • Silly Window Syndrome 방지를 위해 Clark 해법과 Nagle 알고리즘을 사용한다.
  • 실제 전송량은 흐름 제어 윈도우(rwnd)와 혼잡 제어 윈도우(cwnd) 중 작은 값으로 결정된다.