[네트워크] TCP의 원활한 통신을 위한 3가지 노력(흐름제어, 혼잡제어, 오류제어)

앞선 포스트에서 TCP가 원활한 소통을 위해 흐름제어, 혼잡제어, 오류제어라고 불리는 전송제어방법을 사용하고 있다고 했다. 이번 포스트에서는 각 제어방법들이 가지고 있는 여러가지 메커니즘을 알아보자.

🤔 원활한 소통이란?
송수신측의 상태, 네트워크 상태를 고려하여 송신하는 데이터의 양을 조절하고 패킷 손실 등의 문제가 생겼을때 빠른 회복을 위해 노력하는 것


흐름제어

흐름제어는 송 수신측의 TCP 버퍼의 크기 차이로 일어날 수 있는 문제를 제어하여 송신하는 데이터양을 조절한다한다. 데이터 송신 시 수신측에서 받을 수 있는 수용량은 정해져있다. 수용량을 넘어서는 데이터는 받을 수 없고 그렇다고 너무 적은 양의 데이터를 보내는 것은 통신비용이 크다. 따라서 수신측의 버퍼크기(수용량) 알고 그에 따라 전송량을 조절한다.
이때 TCP 헤더 중 Window Size(윈도우 크기)를 이용하여 수신측의 수용량을 체크한다.

Stop and Wait

매우 단순한 동작과정을 가진다.

  1. 송신측에서 하나의 패킷을 보낸다.
  2. 수신측에서 패킷을 잘 받았다는 응답(ACK)을 보낸다.
  3. 응답을 받으면 송신측에서 새로운 패킷 하나를 전송한다.

매우 간단한 방식으로 손실된 패킷을 신속하게 재전송할 수 있다. 하지만 데이터의 양이 많아지면 통신비용이 커져 대량의 데이터 전송에 비효율적이다.


Sliding Window

Sliding Window
슬라이딩 윈도우

앞선 Stop-and-wait 방식의 비효율성을 개선한 기법으로 송수신측이 한 번에 전송할 데이터양을 조절하여 통신하는 방법이다. 윈도우를 옆으로 이동시키며 새로 들어온 데이터를 전송하기 때문에 슬라이딩 윈도우라고 부른다.

  1. 연결 수립과정인 3-way-handshake에서 서로의 Window Size를 교환한다.
    • TCP 헤더의 Window Size(윈도우 크기)를 이용해 서로의 버퍼크기를 확인할 수 있다.
  2. 송신측은 결정한 Window Size만큼 패킷을 한 번에 송신한다.
  3. 수신측은 모든 패킷에 하나하나 ACK을 보내지 않고 한번에 응답
    • Delay ACK : 일정기간동안 기다리고 받은 패킷들에 대해 한 번의 ACK으로 응답
    • 현재 가능한 Window Size도 함께 전송
  4. 송신측은 윈도우를 조절하고 송신한다.
    • ACK된 수 만큼 송신측 윈도우를 이동시킨다.
    • 수신측에서 보낸 Window Size를 참고하여 보낼 데이터양을 정하고 송신한다.

물론 패킷의 양을 조절하는 것은 송/수신 Window Size만으로 결정되는 것은 아니다. 현재 네트워크의 상태도 확인(혼잡 제어)하여 최종적으로 한 번에 보낼 패킷의 양을 조절한다.

정리
송신측 윈도우를 ACK된 수만큼 이동시키며 데이터를 한 번에 전송하는 방식(마치 윈도우가 미끌어 지듯이)이다. 최근에 TCP는 기본적으로 Sliding window 방식을 사용하여 흐름제어를 한다.



TCP의 패킷 손실 판별법(오류 복구 기능)

혼잡제어와 오류제어를 보기 전에 TCP가 오류가 생겼다고 판별하는 기법들에 대해서 알아보자.패킷이 송신지에서 출발해서 수신지에 도착하기까지 방대한 네트워크를 지나게 된다. 이 과정에서 여러가지 요인으로 패킷이 손실된다.
TCP는 전송 도중 손실된 패킷이 ‘나 잘못되었어요~’ 라고 스스로 얘기할때까지 마냥 기다리지 않는다. 이는 곧 전송속도로 직결되기 때문이다. 따라서 특정 조건에 만족하였을때 패킷이 손실되었다고 판단한다.

Timeout

일정기간동안 ACK을 받지 못한 상태이다. 보통 RTT(패킷 왕복 시간)의 2~3배로 설정하고 그 기간동안 응답을 받지 못하면 패킷 손실로 판별한다.

3 ACK Duplicate

이름대로 송신측이 같은 패킷의 ACK을 3번 연속으로 받았을 때를 의미한다. TCP에서 수신측은 누적 승인 방식(Cumulative Acknowledgement)을 이용한다. 이 방식은 받은 데이터 중 연속된 데이터의 마지막 번호를 승인번호로 보낸다.
예를 들어, 2번 패킷이 정상도착, 3번 유실, 4,5번 정상 도착한다면 2번이 도착했을때 ACK 3, 4번이 도착했을도 ACK 3, 5번이 도착했을때도 ACK 3을 보낸다. 이때 송신측은 3번 패킷이 유실되었고 판별한다.

💡 NACK(Nagative Acknowledgement)
NACK은 수신측이 송신측에 패킷이 손실되었음을 명시적으로 알리는 메시지이다. 하지만 TCP는 이 메시지를 지원하지 않는다. NACK을 사용하면 NACK 패킷의 손실도 고려해야하는 등 복잡성을 증가시키기 때문이다.

손실 판별법은 곧 오류를 복구하기 위한 조건으로도 사용된다. 아래에서 설명할 오류제어 및 혼잡제어에서는 손실 판별법을 적극 활용한다.



오류제어

TCP는 재전송 기반의 오류제어를 사용한다. 패킷손실을 확인되었거나 전송 도중 패킷을 잃어버렸다고 판단된다면 ARQ를 이용하여 송신측에 재전송을 요구한다.

💡 ARQ(Automatic Repeat reQuest)
데이터 전송과정에서 오류 검출 및 재전송을 수행해주는 역할을 맡는다. ARQ는 흐름제어 기법과 깊은 연관관계를 가진다.


Stop and Wait ARQ

흐름제어의 Stop and Wait과 매칭되는 오류제어 방법이다. 가장 단순하기 때문에 오류제어가 쉽다. 전송한 패킷이 Timeout 시간 안에 오지 않는 경우 패킷을 재전송 한다.
하지만 Sliding Window 기법을 사용하는 현대의 TCP에서는 이와 같은 오류제어 기법은 알맞지 않다.


Go-Back-N(GBN) ARQ

Slidng Window 흐름제어를 사용할 때 적용할 수 있는 오류제어 방법 중 하나이다.
Sliding Window의 경우 여러 패킷을 한 번에 전송하게 된다. 이때 중간 패킷이 손실되었을때 Go-Back-N ARQ는 손실된 패킷순서부터 이 후 패킷에 대해 전부 재전송한다. 손실된 패킷(N)으로 돌아간다는 뜻에서 Go-Back-N 방식이다. Go-Back-N 방식은 간단하지만 성공적으로 받은 이후 패킷들에 대해서도 재전송을 하기때문에 대역폭 사용량의 증가를 초래한다.


Selective-Repeat(SR) ARQ

이름에서부터 느껴지듯이 재전송할 패킷을 선택할 수 있다. Go-Back-N에서 정상전송된 패킷을 모두 무시하게 되는 비효울성을 가진다. 이러한 문제를 해결한 방법이 SR ARQ로 에러난 데이터만을 재전송하는 방식이다.

중간 패킷에 오류가 발생하면 해당 패킷만을 재전송받고 이후 패킷부터 정상적으로 요청(ACK 신호 보내기)한다. 언뜻보면 가장 좋은 방법 같지만 버퍼에 쌓인 데이터가 연속적이지 않다는 단점이 존재한다. 즉, 데이터를 재정렬하기 위해서 추가적인 버퍼가 필요하다.

Go-Back-N ARQ는 네트워크를 다시 사용해야하기 때문에 오버헤드가 크다. 그래서 보통 수신측의 오버헤드를 늘리는 편이 이득이 크기 때문에 SR을 많이 사용한다.

💡 macOS에서 SR ARQ를 사용하는지 확인하기
sysctl net.inet.tcp | grep sack을 이용했을때 1 값이 출력될 경우 SR을 이용하는 것이다.



혼잡제어

데이터의 양이 라우터가 처리할 수 있는 양을 초과하다면 패킷 손실이 일어난다. 이때 처리하지 못한 데이터를 계속 재전송하고 네트워크가 혼잡상태가 된다. 이런 문제를 TCP는 혼잡제어를 이용해 원활한 통신이 이뤄지게 돕는다. 혼잡제어는 네트워크의 혼잡상태를 캐치하여 전송하는 데이터양을 조절하는 방법이다. 혼잡제어는 혼잡을 회피하는 법과 이를 이용하여 혼잡을 제어하는 정책으로 나누어진다.

💡 용어설명 - RTT(Round Trip Time)
패킷왕복시간을 뜻하는 용어로 패킷을 송신 후 ACK가 도착할때까지 걸리는 시간을 의미한다.

💡 용어설명 - MTU(Maximum Transmission Unit)
한 번 통신 때 보낼 수 있는 최대 단위.

💡 용어설명 - MSS(Maximum Segment Size)
한 세그먼트에 최대로 보낼 수 있는 데이터의 양. 즉, MTU - (TCP, IP의 헤더 및 옵션 길이) 이 값을 초기 혼잡윈도우의 크기로 사용한다.

💡 혼잡윈도우
송신측에서 최종적으로 보낼 패킷의 양을 정할때 수신측이 보내준 윈도우 크기 말고도 네트워크 상황을 고려해서 정한 혼잡 윈도우도 함께 고려하여 두 윈도우 중 더 작은 값을 사용한다. 초기값은 MSS로 지정하여 통신을 하며 이후 혼잡 윈도우 크기를 갱신한다. 다시 말해 혼잡제어는 혼잡윈도우 크기를 정하는 방식과 같다고 말할 수 있다.


혼잡 회피 기법

AIMD(Additive Increase / Multicative Decrease)

  • 합 증가 / 곱 감소 방식
    • 네트워크에 문제가 없으면 혼잡윈도우 크기를 1증가, 문제가 있다면 혼잡윈도우 크기를 반으로 줄인다.
  • 공평함
    • 네트워크에 늦게 합류한 노드들도 결국 다른 노드들과 비슷한 혼잡윈도우 크기를 가지게 됨
    • 혼잡윈도우가 큰 노드들은 문제가 생길 확률이 크기 때문
  • 가속력이 낮음
    • 1씩 증가시키기 때문에 제대로된 속도가 나올 때까지 시간이 걸림
    • 대역폭이 넓어지고 혼잡 발생 빈도가 줄어듬에 따라 단점이 부각됨

Slow Start

  • 지수 증가 / 초기화 방식
    • 문제가 없다면 혼잡윈도우를 지수적으로 증가, 문제가 있다면 1로 초기화
  • 빠른 혼잡 윈도우 크기 증가
    • 빠르게 최대 전송 속도를 낼 수 있는 지점에 도달할 수 있다.
  • 상대적으로 혼잡 발생 빈도가 높음

💡 ssthresh(Slow Start Threshold)
Slow Start의 경우 기하급수적으로 늘어나서 제어가 힘들다. 혼잡이 예상되는 상황에서 이러한 방식은 비효율적이다. 따라서 임계점(Threshold)를 두고 그 이상부터는 Slow Start가 아닌 AIMD를 사용한다. 이 때 이 임계점을 ssthresh라고 부른다.


혼잡제어 정책

혼잡 제어 정책에는 TCP Tahoe, TCP Reno, TCP New Reno, Cubic 등 다양한 정책이 존재한다. 이 정책들은 공통적으로 혼잡 발생 시 윈도우 크기를 줄이고 혼잡을 회피하기 위해 노력한다. 이 중 가장 대표적인 정책인 Tahoe, Reno는 위에서 설명한 AIMD와 Slow Start를 적절하게 사용하여 혼잡을 제어한다.

혼잡제어정책 - TCP Tahoe

TCP Tahoe
출처 : https://evan-moon.github.io/2019/11/26/tcp-congestion-control/

TCP Tahoe의 혼잡 회피 방식은 다음과 같다.

  1. Slow Start로 시작
  2. ssthresh 만난 이후 AIMD를 이용

이때 혼잡이 발생(3 ACK Duplicated, Timeout) 했다고 판단된다면 두가지 행동을 취한다.

  1. 혼잡윈도우 초기화(크기 1로)
  2. ssthresh 재설정(혼잡이 발생한 윈도우 크기의 * 0.5)

혼잡 상황을 기억하고 합리적으로 조잘하는 정책이다. 하지만 혼잡 상황 발생시 처읍부터 시작해야한다는 단점이 존재한다.


혼잡제어정책 - TCP Reno

TCP Reno
출처 : https://evan-moon.github.io/2019/11/26/tcp-congestion-control/

Reno 또한 Tahoe와 마찬가지로 Slow Start로 시작하고 이후 AIMD를 이용한다.

  1. Slow Start로 시작
  2. ssthresh 만난 이후 AIMD를 이용

하지만 혼잡 발생시에는 Tahoe와 다르게 동작한다. 어떤 오류이냐에 따라 다른 행동을 취한다.

  1. 혼잡윈도우 수정
    • 3 ACK Duplicated : AIMD 방식대로 혼자윈도우 값을 반으로 줄인다.(빠른 회복)
    • Timeout : Slow Start 방식대로 혼잡윈도우를 1로 초기화한다.
  2. ssthresh 재설정
    • 3 ACK Duplicated : 줄어든 혼잡윈도우와 같은 값
    • Timeout : 변경하지 않음

Tahoe보다 이후에 나온만큼 더 개선된 정책이라고 볼 수 있다. 오류 상황에 대해 우선순위를 두어 전송속도를 빠르게 회복 할 수 있다는 것이 Reno의 장점이다.


참고

카테고리:

업데이트:

댓글남기기