네트워크

TCP flow control

teo_99 2024. 3. 29. 22:35

TCP(Transmission Control Protocol)에는 크게 세 가지 제어 기능이 존재한다. 하나는 전송되는 패킷의 양을 조절하는 흐름제어, 하나는 패킷이 잘 도착했는지를 확인하고 오류가 발생했을 때 재전송할 수 있게 해주는 오류제어, 마지막 하나는 네트워크의 혼잡을 피하기 위한 혼잡제어다. 이번 아티클에서는 그 중 흐름제어에 대해 살펴본다. 각각 양이 많아 오류제어와 혼잡제어는 추후 다른 아티클로 작성하겠다.
 

흐름제어(flow control)

통신에서 주로 활용하는 기술이 버퍼다. 이는 여러 요소(장치, 프로그램)등이 처리하는 속도나 크기, 단위가 다르기 때문이다. 따라서 어떠한 완충기 역할을 하는 버퍼를 둬서 통신하게 된다.
 
우리가 사용하는 PC도 네트워크 통신을 위한 NIC(Network Interface Controller) 가 존재하는데, NIC도 송신, 수신 버퍼를 가지고 있다. 참고로, 이러한 버퍼의 형태로는 자주 사용되는게 링 버퍼(Ring Buffer)라는 Circular Queue 형태의 자료구조이다.
 
이러한 버퍼의 용량은 통신하는 주체마다 다를 것이다. 가령 A PC와 B PC가 통신하고 있다면 A PC의 버퍼 크기, B PC의 버퍼 크기가 다를 수 있다. 또한, 버퍼의 내용을 처리하는 속도 또한 다를 수 있다.
 
TCP 통신에서는 따라서, 송신측에 어떠한 정보를 제공해서 적절한 시기에 맞춰 패킷을 전송하도록 유도하며 이를 흐름제어라고 한다. 만약 상대방의 버퍼 크기, 처리 속도를 고려하지 않고 패킷을 송신한다면, 버퍼의 크기를 넘쳐 유실되는 상황 등이 발생할 것이다.
 
TCP는 Sliding Window 방식을 사용해서 흐름제어를 진행한다. 이는 나중에 자세히 살펴보겠지만, 특정한 윈도우 크기만큼을 타겟으로 하여 패킷을 전송하는 기법을 의미한다.
 
이러한 Sliding Window 기법을 보다 쉽게 이해하기 위해선, 가장 기본적인 통신 방식인 Stop and wait 방식에 대해 알아볼 필요가 있으니 먼저 소개하도록 하겠다.
 

Stop and wait (정지대기) 방식

Stop and wait은 말 그대로, 정지하고 대기하는 방식이다. 송신측에서 1개의 프레임을 송신하고, 수신측에서는 프레임의 에러 유무에 따라 ACK 혹은 NAK(Negative ACK)를 보낸다. 이후 송신측이 ACK를 수신하면, 그제서야 다음 패킷을 전송한다. 만약 NAK를 수신하거나 타이머가 소모될때까지 ACK를 수신하지 못한다면 에러 발생으로 간주하고 프레임을 재전송한다.
 
또한, Stop and wait 방식을 사용해 흐름제어를 하는 경우에는 '수신자는 패킷을 소모한 뒤 ACK 응답을 해야 한다' 라는 규칙이 있기 때문에, ACK응답이 되는 시점에는 버퍼가 비어있음을 확신할 수 있다. 따라서 버퍼가 넘치거나, 흐름 제어가 실패하는 경우는 없다.

 
다만, 단순히 위와 같은 방식을 취하는 경우 여러 문제가 발생할 수 있는데, 가령 대표적인 문제로는 패킷의 소실 문제(Lost Data / Lost Acknowledgment)가 있다.
 
이는 ACK 응답 혹은 송신측의 데이터가 유실될 수 있음을 의미하는데, 위와 같은 '보내면 ACK/NAK 응답이 올 때까지 기다린다'라는 규칙은 무한정 대기하게 될 수 있어 위험하다.
 
이러한 Stop and wait 방식에서는 이러한 문제점을 총 2가지 방법으로 보완할 수 있다. 하나는 Time-out이며, 하나는 Sequence Number이다.
 
1. Time Out

송신측에서 정해진 시간을 두고, 해당 시간 내에 ACK 응답이 오지 않는 경우에 '송신에 실패했다'고 간주할 수 있다. 이렇게 타이머를 두면 무한정 대기하는 상황은 사라진다. 다만 적절히 타이머 값을 설정하는게 Issue가 될 것이다.
 
조금 덧붙이자면, TCP에서는 이러한 타임아웃 값(Retransmission Timeout, RTO)을 여러 변수를 통해 결정한다. 이에 대한 내용은 다음에 기회가 되면 다루기로 하겠다. (참고: 링크)
 
2. Sequence Number

TCP를 공부해본 사람이라면 Sequence Number에 친숙할 것이다. Sequence Number란, '데이터 잘 받았고, 어디부터 줘' 라는 정보를 송신자에게 제공함으로서 패킷의 정상 도착 여부를 판단할 수 있다. 
 

Sliding Window 방식

다만 Stop and wait 방식은 TCP에서 사용되지 않는다. 가장 큰 문제는 성능 때문이다. 동시 처리를 진행할 수 없고 한번에 하나의 패킷만 전송할 수 있다. 또한 패킷을 보낼때마다 네트워크 비용이 선형적으로 증가하기도 한다.
 
물론 성능보다는 신뢰성이 아주 중요한 환경(테스트 등)이라면 Stop and wait를 사용하겠지만, TCP에서는 사용하기가 어려운 것이 현실이다. 따라서 TCP에서는 Sliding Window라는 프로토콜을 사용하는데, 이는 마치 창문을 옮기는 것처럼 데이터의 특정 부분에만 집중해 처리하는 방법이다.
 
Sliding Window 프로토콜은 한 번에 여러 패킷을 전송할 수 있게 해준다. 한번에 여러 패킷을 전송한다는 것은, 조금 더 구체적으로 말하면 'ACK를 받지 않고도 여러 패킷을 전송할 수 있다' 는 것을 의미한다. 
 
이런게 어떻게 가능한걸까? 앞서 각각의 디바이스는 송수신을 위한 버퍼가 존재한다고 했다. 이러한 버퍼 사이즈를 상대방에게 알려준다면, 송신측에서 한번에 얼만큼의 패킷을 보낼 수 있는지 계산할 수 있을것이다. 
 
TCP라는 프로토콜은 3-way handshake를 통해 제어정보를 교환하고 논리적인 연결을 맺는다. 그리고 이 과정에서 가용 버퍼 크기 역시 송신측에 전달된다. 송신측은 이러한 버퍼 크기를 윈도우 사이즈로 책정하고, 해당 윈도우 사이즈를 기반으로 송신을 진행하게 된다.
 

https://www.techtarget.com/searchnetworking/definition/sliding-windows

 
위 그림을 통해 제대로 이해해보자. 11번 패킷부터 30번 패킷까지 수신측에 전송해야 하는 상황이다. 윈도우 사이즈를 10이라고 통보받았다면, 위 그림과 같이 한 번에 10개의 패킷만 전송할 수 있다. 
 
만약 보낸 패킷에 대한 ACK 응답이 도착했다면, 윈도우를 우측으로 옮겨 다음 패킷을 윈도우 안에 포함시킨다. 위 그림에서는 26번 패킷이 되겠다.
 
이러한 Sliding Window의 특성을 고려하면, 송신측 버퍼에 존재하는 패킷은 다음과 같이 네가지의 카테고리로 구분할 수 있다. 그리고 이 중 윈도우가 다루는 카테고리는 2번, 3번이다. 

  1. Sent and acknowledged (전송하고 ACK도 받았음)
  2. Sent but unacknowledged (전송은 했지만 ACK를 받지 못했음)
  3. Not sent but ready to receive (전송하지 못했지만 수신측이 받을 준비가 되어있음)
  4. Not sent and not ready to receive (전송하지도 못했고, 수신측이 받을 준비도 되지 않았음)

Slding Window 방식은 위 규칙이 끝이 아니다. 수신측은 버퍼에 있는 패킷을 필요할 때 소모하는데, 송신측에서는 이 행위를 알 방법이 없다.
 
예를 들어 PC로 구글에 접속했다고 해보자. 구글 로고, 회원 정보 등이 패킷으로 전달되어 우리의 NIC 버퍼에 도착할 것이다. 이후 브라우저는 수신한 패킷을 읽어 여러 처리 과정(5~7계층)을 거치고 우리에게 보여줄 것이다.
 
하지만 구글 서버 입장에서는 이 과정을 알 방법이 없다. 버퍼를 언제 읽었는지, 읽었다면 얼마나 읽었는지에 대한 예측이 불가능한 것이다. 따라서 '버퍼에 이만큼의 여유공간이 생겼다' 라는 사실을 알려줄 또 다른 방법이 필요하다.
 
따라서 수신측은, 송신측에게 rwnd, 즉 윈도우 사이즈를 지속적으로 통보하면서 업데이트된 윈도우 크기를 제공한다. 이후 송신측은 해당 값에 맞춰 윈도우 크기를 재조정한다. 아래는 지금까지 설명한 모든 내용을 조합한 그림이다.
 

Data Communications and Networking, 5th Edition

 

마치며

TCP의 flow control 기법을 살펴봤다. 생각보다 내용이 쉽지는 않았는데, 계속 혼잡 제어 메커니즘과 헷갈려서 그랬던 것 같다. 추후 TCP congestion control 아티클을 정리하면서 보완하도록 하겠다.
 

참고자료

 
http://www.ktword.co.kr/test/view/view.php?no=737
http://www.ktword.co.kr/test/view/view.php?no=1355
http://www.ktword.co.kr/test/view/view.php?m_temp1=848&id=843
https://www.baeldung.com/cs/networking-stop-and-wait-protocol
https://www.geeksforgeeks.org/stop-and-wait-arq/
https://www.techtarget.com/searchnetworking/definition/sliding-windows
https://www.youtube.com/watch?v=LnbvhoxHn8M
http://www.ktword.co.kr/test/view/view.php?m_temp1=1469&id=746
https://evan-moon.github.io/2019/11/22/tcp-flow-control-error-control/