사이 좋게 네트워크를 나눠 쓰는 방법, TCP의 혼잡 제어

    사이 좋게 네트워크를 나눠 쓰는 방법, TCP의 혼잡 제어


    혼잡 제어란, 말 그대로 네트워크의 혼잡 상태를 파악하고 그 상태를 해결하기 위해 데이터 전송을 제어하는 것을 이야기한다.

    네트워크는 워낙 광대한 블랙박스이기 때문에 정확히 어디서 어떤 이유로 전송이 느려지는지는 파악하기 힘들지만, 단순히 느려지고있다 정도는 각 종단에서도 충분히 파악할 수 있다. 그냥 데이터를 보냈는데 상대방으로부터 응답이 늦게 오거나 안오면 뭔가 문제가 있다는 것이니 말이다.

    이때 위에서 이야기한 흐름 제어나 오류 제어 기법들만을 사용하다보면 자연스럽게 재전송이라는 작업이 계속 반복될 수 밖에 없다.

    이게 한 두 녀석이 그러면 별 문제가 안될지도 모르지만, 네트워크는 워낙 다양한 친구들이 함께 이용하는 공간이다보니까 한번 네트워크가 뻑나기 시작하면 여기저기서 나도 재전송할꺼야!가 반복되면서 문제가 점점 악화될 것이다. 이를 네트워크의 혼잡 붕괴라고 부른다.

    그래서 이런 식으로 네트워크의 혼잡 상태가 감지되면, 이런 최악의 상황을 최대한 회피하기 위해 송신 측의 윈도우 크기를 조절하여 데이터의 전송량을 강제적으로 줄이게 되는데, 이것이 바로 혼잡 제어인 것이다.

    혼잡 윈도우(Congestion Window, CWND)

    필자는 패킷의 흐름과 오류를 제어하는 TCP 포스팅에서 TCP의 흐름 제어를 설명할 때 송신 측의 윈도우 크기는 수신 측이 보내준 윈도우 크기와 네트워크 상황을 함께 고려해서 정해진다는 이야기를 했었다.

    송신 측은 자신의 최종 윈도우 크기를 정할 때 수신 측이 보내준 윈도우 크기인 수신자 윈도우(RWND), 그리고 자신이 네트워크의 상황을 고려해서 정한 윈도우 크기인 혼잡 윈도우(CWND) 중에서 더 작은 값을 사용한다.

    즉, 아래 후술할 혼잡 제어 기법들이 늘였다 줄였다 하는 윈도우 크기는 송신 윈도우가 아니라 송신 측이 가지고 있는 혼잡 윈도우 크기인 것이다.

    참고로 RWND나 CWND가 그냥 윈도우라는 의미를 가지고 있기 때문에, 슬라이딩 윈도우에서 사용하는 윈도우와 같은 개념이라고 생각할 수 있는데, 얘네는 각각 수신자 윈도우 크기와 혼잡 윈도우 크기를 의미하는 숫자다.

    그럼 통신 중에는 어떻게든 네트워크의 혼잡도를 파악해서 혼잡 윈도우 크기를 유연하게 변경한다고 해도, 통신을 시작하기 전에는 이 혼잡 윈도우 크기를 어떻게 초기화하는 것일까?

    혼잡 윈도우 크기 초기화하기

    통신을 하는 중간에는 ACK가 유실된다거나 타임아웃이 난다거나 하는 등의 정보를 사용하여 네트워크의 혼잡 상황을 유추할 수 있지만, 통신을 시작하기 전에는 그런 정보가 하나도 없기 때문에 혼잡 윈도우의 크기를 정하기가 조금 애매하다. 여기서 등장하는 것이 바로 MSS(Maximum Segement Size)이다.

    MSS는 한 세그먼트에 최대로 보낼 수 있는 데이터의 양을 나타내는 값인데, 대략 다음과 같은 계산을 통해 구할 수 있다.

    MSS = MTU - (IP헤더길이 + IP옵션길이) - (TCP헤더길이 + TCP옵션길이)

    여기서 등장하는 MTU(Maximum Transmission Unit)라는 친구는 한번 통신 때 보낼 수 있는 최대 단위를 의미한다.

    즉, MSS는 한번 전송할 때 보낼 수 있는 최대 단위가 정해져있는 상황에서 IP 헤더, TCP 헤더 등 데이터가 아닌 부분을 전부 발라내고 진짜 데이터를 담을 수 있는 공간이 얼마나 남았는지를 나타내는 것이다.

    mtu setting 시스템 환경 설정에서 잘 찾아보면 기본 MTU를 변경할 수 있는 설정이 있다

    OSX 같은 경우는 MTU 기본 값으로 이더넷 표준인 1500 bytes가 설정되어있다. 이때 TCP와 IP의 헤더크기가 각각 20 bytes라고 하면 MSS는 1500 - 40 = 1460 bytes가 되는 것이다.

    송신 측은 처음 통신을 시작할 때 이렇게 계산한 MSS를 사용하여 혼잡 윈도우의 크기를 1 MSS로 설정한다. 이후 통신을 하면서 네트워크의 혼잡 상황을 고려하며 혼잡 윈도우 크기를 증가시키거나 감소시키는 것이다.

    혼잡 회피 방법

    할배 프로토콜인 TCP는 지난 50년 동안 지속적으로 개선된 다양한 혼잡 제어 정책들을 가지고 있다.

    각 혼잡 제어 정책은 어떤 시점을 혼잡한 상태라고 파악할 것인지, 혼잡 윈도우 크기를 줄이거나 키우는 방법을 개선하여 점점 발전해왔지만, 가장 기본적인 혼잡 제어 방법은AIMDSlow Start라는 혼잡 회피 방법을 상황에 맞게 조합하는 것이다.

    그래서 이 포스팅에서도 기본적인 혼잡 회피 방법인 AIMDSlow Start를 먼저 설명하고나서 대표적인 혼잡 제어 정책인 TahoeReno를 이야기할 것이다.

    AIMD

    AIMD(Additive Increase / Multicative Decrease) 방식은 우리 말로 직역하면 합 증가 / 곱 감소 방식이라는 뜻이다. 즉, 네트워크에 아직 별 문제가 없어서 전송 속도를 더 빠르게 하고 싶다면 혼잡 윈도우 크기를 1씩 증가시키지만, 중간에 데이터가 유실되거나 응답이 오지 않는 등의 혼잡 상태가 감지되면 혼잡 윈도우 크기를 반으로 줄인다.

    늘어날 때는 ws + 1, 줄어들 때는 ws * 0.5이므로 말 그대로 합 증가 / 곱 감소인 것이다. 이렇게 늘어날 때는 선형적으로 조금씩 늘어나고 줄어들 때는 반으로 확 줄어드는 AIMD 방식의 특성 상, 이 방식을 사용하는 연결의 혼잡 윈도우 크기를 그래프로 그려보면 다음과 같은 톱니 모양이 나타난다.

    aimd

    이 방식은 굉장히 심플하지만 생각보다 공평한 방식이다. 예를 들어 여러 친구들이 이미 네트워크를 점유하고 있는 상태에서 한 친구가 뒤늦게 이 네트워크에 합류했다고 생각해보자.

    당연히 나중에 진입한 쪽의 혼잡 윈도우 크기가 작기 때문에 처음에는 불리하다. 그러나 네트워크가 혼잡해지면 혼잡 윈도우 크기가 작은 놈보다 혼잡 윈도우 크기가 큰 놈이 무리하게 데이터를 왕창 보내려다가 유실될 확률도 더 크다.

    이런 상황이라면 네트워크에 일찍 참여해서 이미 혼잡 윈도우 크기가 큰 놈은 자신의 윈도우 크기를 줄여서 혼잡 상황을 해결하려고 할 것이고, 이때 남은 대역폭을 활용하여 나중에 들어온 놈들이 자신의 혼잡 윈도우 크기를 키울 수 있는 것이다.

    그런 이유로 시간이 가면 갈수록 네트워크에 참여한 순서와 관계 없이 모든 호스트들의 윈도우 크기가 평행 상태로 수렴하게 되는 것이다.

    그러나 AIMD의 문제점은 네트워크 대역이 펑펑 남아도는 상황에도 윈도우 크기를 너무 조금씩 늘리면서 접근한다는 것이다. 그런 이유로 AIMD 방식은 네트워크의 모든 대역을 활용하여 제대로 된 속도로 통신하기까지 시간이 조금 걸린다.

    윈도우 크기가 수렴한다고?

    사실 AIMD는 워낙 단순한 방식이다 보니 간단한 예제를 통해 네트워크의 상황을 재현해볼 수도 있다. 이렇게 직접 구현해보면 말로만 들었을 때는 애매한 호스트들의 윈도우 크기가 평행으로 수렴한다는 것이 어떤 의미인지 직접 눈으로 확인해볼 수도 있다.

    코드를 직접 포스팅에 첨부하려고 했는데, 최대한 실제 네트워크와 비슷한 환경을 재현하려고 하다보니 코드가 생각보다 길어져서 결과만 첨부하려고 한다. 우선 필자의 실험 방식은 다음과 같다.

    1. 네트워크의 혼잡도 최고치는 50이며, 이 혼잡도는 네트워크에 참여한 호스트들이 가진 혼잡 윈도우 크기의 총합으로 결정된다.
    2. 네트워크에 호스트를 300ms에 하나씩 생성해서 추가한다.
    3. 각 호스트는 100 ~ 200ms 마다 네트워크의 현재 혼잡도를 계산하고 자신의 윈도우 크기를 조절한다.
    4. 각 호스트는 윈도우 크기 조절을 300회까지 수행하고 네트워크를 빠져나간다.

    참고로 이 테스트에는 약간의 허점이 있다. 필자는 호스트들의 연산에 동시성을 부여하기 위해 setInterval을 사용했는데, setInterval의 콜백도 이벤트 루프를 거친 후 결국 콜 스택을 쌓아가며 수행되기 때문에 실제 네트워크처럼 호스트들의 연산에 완전한 병렬성이 보장되지는 않는다.

    그래서 테스트의 목적인 네트워크에 늦게 참여한 호스트들도 충분한 혼잡 윈도우 크기를 가질 수 있는지 확인하는 용도로는 딱히 별 문제가 없기 때문에 그대로 진행했다.

    이에 대한 자세한 개념은 로우 레벨로 살펴보는 Node.js 이벤트 루프에 자세히 설명되어있으니 확인해보도록 하자.

    어쨌든 이렇게 테스트를 해보고 나온 데이터들을 시계열 그래프로 시각화해보았다. 먼저 각 호스트들의 혼잡 윈도우 크기 변화를 살펴보자.

    cwnd chart

    그래프를 살펴보니 먼저 네트워크에 들어와있던 호스트들과 뒤늦게 네트워크에 합류한 호스트들의 혼잡 윈도우 크기에 그렇게 큰 차이가 없는 모습을 확인할 수 있었다. 마지막에 혼자 치솟고 있는 친구는 다른 호스트들이 다 빠져나간 후에 네트워크가 텅텅 비어서 그런 것이다.

    이런 식으로 시각화를 해보니 각 호스트들의 혼잡 윈도우 크기가 큰 차이 없이 어느 부분에 수렴하고 있다는 것을 확인할 수 있었다. 그렇다면 네트워크에 들어와있는 호스트들의 전체 윈도우 크기는 시계열 그래프로 시각화해보면 어떨까?

    total cwnd chart

    확실히 네트워크 내 전체 호스트의 혼잡 윈도우 크기를 보니 필자가 설정해놓은 네트워크 혼잡도인 50을 넘어가지 않는 선에서 자기들끼리 엎치락뒤치락 윈도우 크기를 조절하고 있는 모습을 확인할 수 있었다.

    마찬가지로 이 그래프를 확인해보아도 호스트들이 네트워크에 입퇴장하는 초반부와 후반부를 제외하면 중간 즈음에서는 각 호스트들의 혼잡 윈도우 크기가 크게 차이나지 않는다.

    이렇게 직접 상황을 시뮬레이션해보고 데이터를 확인하니 말로만 들었을 때는 잘 와닿지 않았던 윈도우 크기가 평행 상태로 수렴한다는 이야기를 조금 더 잘 이해할 수 있었다.

    만약 이 코드를 직접 실행시켜서 모든 과정을 확인하고 싶은 분들은 필자가 올려놓은 깃허브 레파지토리에서 클론받아서 실행시켜보면 된다.

    Slow Start

    위에서 이야기했듯이 AIMD 방식은 윈도우 크기를 선형적으로 증가시키기 때문에, 제대로 된 속도가 나오기까지 시간이 조금 걸리는 단점이 있다.

    사실 요즘에는 네트워크의 대역폭이 워낙 넓고 통신 인프라도 좋다보니 예전에 비해서 네트워크의 혼잡 상황 발생하는 빈도가 많이 줄어들었기 때문에, 혼잡이 발생하지도 않았는데 제대로 속도를 내는데까지 오래걸리는 AIMD 방식의 단점이 점점 부각되었다.

    반면, Slow Start는 기본적인 원리는 AIMD와 비슷하지만 윈도우 크기를 증가시킬 때는 지수적으로 증가시키다가 혼잡이 감지되면 윈도우 크기를 1로 줄여버리는 방식이다.

    이 방식은 보낸 데이터의 ACK가 도착할 때마다 윈도우 크기를 증가시키기 때문에 처음에는 윈도우 크기가 조금 느리게 증가할지 몰라도, 시간이 가면 갈수록 윈도우 크기가 점점 빠르게 증가한다는 장점이 있다.

    기본적으로 AIMD와 어떤 방식으로 윈도우 크기를 증가시키냐, 감소시키냐의 차이만 존재하기 때문에, 위에서 작성한 예시에서 윈도우 사이즈를 변경하는 부분만 변경하면 Slow Start의 차트를 그려볼 수 있다.

    slow start chart

    데이터를 확인해보면 필자가 정해놓은 네트워크 혼잡도의 최고치인 50보다 호스트들의 혼잡 윈도우 크기가 커지는 경우가 발생하는데, 이건 자바스크립트가 setInterval의 콜백을 처리하는 과정에서 발생한 문제이므로 실제 네트워크에서는 이러지 않는다. (애초에 혼잡도를 이렇게 간단히 계산하지도 않는다)

    AIMD와 Slow Start 그래프의 가장 큰 차이점은 네트워크 혼잡도의 최고치에 도달하는 시간이다.

    AIMD는 윈도우 크기를 선형적으로 키워나가기 때문에 다른 호스트들이 새로 들어올 때 많은 부분을 점유할 수 없었지만, Slow Start 방식을 사용하면 처음에는 윈도우 크기를 느리게 키우다가 아직 여유가 있다고 판단되면 지수적으로 팍팍 증가시킬 수 있으므로 결과적으로 AIMD보다 윈도우 크기를 더 빠르게 키울 수 있는 것이다.

    최근의 TCP에서 사용하고 있는 TahoeReno와 같은 정책들은 AIMD와 Slow Start를 적절히 섞어서 사용하되, 네트워크 혼잡 상황이 발생했을 때 어떻게 대처하는 지에 따라서 나뉘어지게 된다.

    혼잡 제어 정책

    TCP에는 Tahoe, Reno, New Reno, Cubic, 최근 나온 Elastic-TCP까지 일일히 나열하기에도 숨 가쁠 정도로 다양한 혼잡 제어 정책이 존재한다.

    이러한 혼잡 제어 정책들은 공통적으로 혼잡이 발생하면 윈도우 크기를 줄이거나, 혹은 증가시키지 않으며 혼잡을 회피한다라는 전제를 깔고 있고, 최근에 나온 방법은 예전 방법들에 비해서 더 똑똑하게 혼잡 상황을 감지하고 네트워크 대역을 최대한 빠르고 안전하게 활용할 수 있는 방향으로 개발된 것이다.

    하지만 이 친구들을 이 포스팅에서 전부 설명하기는 힘드니, 이 중에서 가장 대표적이고 유명한 정책인 TahoeReno를 위주로 혼잡 제어의 기본 방식에 대해서 설명하려고 한다.

    TahoeReno는 기본적으로 처음에는 Slow Start 방식을 사용하다가 네트워크가 혼잡하다고 느껴졌을 때는 AIMD 방식으로 전환하는 방법을 사용하는 정책들이다.

    taheo reno 붉은 색이 Tahoe, 녹색이 Reno의 윈도우 크기를 나타낸다

    위 그래프의 Y축은 혼잡 윈도우, X축은 시간으로 하여 Tahoe와 Reno의 작동 방식을 설명하고 있다. 본격적으로 Tahoe와 Reno를 알아보기에 앞서, 일단 이 그래프가 뭘 설명하고 있는지 빠르게 이해하기 위해 간단한 용어를 몇 가지만 짚고 넘어가도록 하겠다.

    그래프가 꺾이는 곳에 적혀있는 3 ACK Duplicated, Timeout과 그래프의 상승 폭이 변하고 있는 Threshold에 대해서 알아보도록 하자. (그림의 철자가 이상한 이유는 영어가 아니라 이탈리아어라서 그런 것이니 신경쓰지말자)

    3 ACK Duplicated, Timeout 일단 두 방식 모두 3 ACK DuplicatedTimeout이라는 두 가지 시나리오가 발생하면 윈도우 크기를 줄이는 것을 확인해볼 수 있다. 즉, 이 두 상황이 혼잡 제어 정책들이 혼잡 발생을 감지하는 기본적인 상황인 것이다.

    타임아웃은 말 그대로 여러가지 요인으로 인해 송신 측이 보낸 데이터 자체가 유실되었거나, 수신 측이 응답으로 보낸 ACK가 유실되는 경우를 뜻하는 것이다.

    3 ACK Duplicated, 즉 송신 측이 3번 이상 중복된 승인 번호를 받은 상황 또한 정상적으로 데이터가 전송된 상황은 아닌데, 수신 측은 자신이 정상적으로 처리한 데이터에 대해서만 ACK를 보내기 때문에 동일한 승인 번호를 세 번이나 받은 상황은 어떤 이유로 인해 수신 측이 특정 시퀀스 번호 이후의 데이터를 제대로 처리하지 못한 상황이라고 볼 수 있다.

    단, 패킷 전송 방식을 사용하는 TCP의 특성 상 수신 측이 받는 패킷의 순서가 늘 순서대로 받으리라는 보장은 없으므로, 한 두개의 중복 승인 번호가 발생했다고 해서 바로 네트워크가 혼잡하다고 판단하지는 않는다.

    fast transmit

    TCP는 현재까지 누적된 정상 데이터 중 가장 마지막 데이터에 대한 승인 번호를 보내주는 누적 승인 방식(Cumulative Acknowledgement)를 사용하기 때문에, 송신 측이 같은 승인 번호를 계속 중복해서 받는다면 해당 데이터까지는 정상적으로 전송되었으나 그 이후부터는 뭔가 문제가 발생했다고 알 수 있는 것이다.

    이때 3번의 중복 승인 번호로 인해 송신 측이 바로 해당 승인 번호에 해당하는 데이터를 전송하고나면 수신 측은 Go back N이나 Selective Repeat과 같은 오류 제어 방식에 따라서 다음에 어떤 패킷부터 보내줘야하는지 알려줄 것이다.

    이런 상황일 경우, 송신 측은 자신이 설정한 타임아웃 시간이 지나지 않았어도 바로 해당 패킷을 재전송할 수 있는데, 이 기법을 빠른 재전송(Fast Transmit)이라고 한다.

    만약 빠른 재전송 기법을 사용하지 않는다면, 송신 측은 자신이 설정한 타임아웃 시간이 지난 이후에야 대처할 수 있기 때문에 에러난 데이터를 재전송하기까지 시간이 낭비되게 될 것이다.

    Slow Start 임계점(ssthresh)

    Tahoe와 Reno를 비교하는 그래프를 보면 Threshold(임계점)이라는 단어가 꽤 많이 등장하고 있는 것을 볼 수 있는데, 이 임계점은 Slow Start Threshold(ssthresh)를 뜻하는 것으로, 여기까지만 Slow Start를 사용하겠다라는 의미를 가진다.

    ssthresh

    이 값을 사용하는 이유는 Slow Start를 사용하며 윈도우 크기를 지수적으로 증가시키다보면 어느 순간부터는 윈도우 크기가 기하급수적으로 늘어나서 제어가 힘들어지기도 하고, 네트워크의 혼잡이 예상되는 상황에서는 빠르게 값을 증가시키는 것보다 돌다리 두들겨 건너듯이 조금씩 증가시키는 편이 훨씬 안전하기 때문이다.

    쉽게 생각해서 현재 윈도우 크기가 10이고 현재 네트워크에 남은 공간이 15라고 할 때, Slow Start 방식으로 윈도우 크기를 증가시키면 20을 넘겨버리지만 합 증가를 하게 되면 앞으로 한 5번은 윈도우 크기를 더 증가시킬 수 있다.

    그래서 특정한 임계점(Threshold)를 정해놓고, 임계점을 넘어가게되면 AIMD 방식을 사용하여 선형적으로 윈도우 크기를 증가시킨다. 그래서 이 임계점을 칭하는 단어가 ssthresh(Slow Start Threshold)인 것이다.

    송신 측은 본격적인 통신이 시작하기 전에 ssthresh 값을 자신의 혼잡 윈도우의 절반 크기인 0.5 MSS으로 초기화하고, 이후 어떤 혼잡 제어 방법을 사용하냐에 따라 다르게 대처하게된다.

    TCP Tahoe

    TCP Tahoe는 Slow Start를 사용한 혼잡 제어 정책의 초기 버전으로, 위에서 설명한 빠른 재전송(Fast Transmit) 기법이 처음으로 도입된 방법이다. Tahoe 이후의 혼잡 제어 정책은 Tahoe와 마찬가지로 빠른 재전송 기법을 기본으로 사용하되, 효율을 위해 몇 가지 양념을 더 치게 된다.

    참고로 TCP Tahoe는 미국 네바다주에 있는 타호 호수의 이름을 따서 지어졌으므로 그냥 타호라고 읽으면 된다. (타호에가 아니다!)

    lake tahoe 왜 여기서 이름을 따왔는지는 모르겠지만 호수가 이쁘긴 하다

    Tahoe는 처음에는 Slow Start를 사용하여 자신의 윈도우 크기를 지수적으로 빠르게 증가시키다가 ssthresh를 만난 이후부터는 AIMD에서 사용하는 합 증가 방식을 사용하여 선형적으로 윈도우 크기를 증가시킨다.

    그러다가 위에서 언급한 ACK Duplicated나 Timeout이 발생하면 네트워크에 혼잡이 발생했다고 판단하고, ssthresh와 자신의 윈도우 크기를 수정하게 된다. 조금 더 편한 이해를 위해 Tahoe의 혼잡 윈도우 크기 변화 그래프를 살펴보도록 하자.

    tahoe ssthresh

    위 그래프에서 청록색 선은 송신 측의 혼잡 윈도우 크기를, 굵은 검정선은 ssthresh 값을 보여주고 있다. 이 시나리오에서 송신 측의 혼잡 윈도우 크기는 8로 초기화되었고, 그에 따라 ssthresh는 8 * 0.5 = 4로 설정된 것을 볼 수 있다.

    송신 측은 임계점을 만나기 전까지 Slow Start 방식을 사용하여 자신의 윈도우 크기를 지수적으로 증가시키다가 ssthresh를 넘어선 이후부터는 선형적으로 증가시키고 있다. 이때 3 ACK DuplicatedTimeout과 같은 혼잡 상황이 발생하면 어떻게 될까?

    그래프를 보면 가장 처음 혼잡 상황이 발생한 상태의 혼잡 윈도우 크기는 6이다. 이때 송신 측은 ssthresh를 6의 절반인 3으로 변경하고, 자신의 혼잡 윈도우 크기를 다시 1로 변경하는 모습을 확인할 수 있다. 이후 다시 Slow Start로 시작하여 임계점에 도달하면 합 증가로 변경하는 방법을 반복하게 된다.

    즉, 이전에 한 대 맞았던 지점을 기억하고 그 지점이 가까워지면 조금씩 몸을 사리는 원리인 것이다. 이런 접근법은 나름 합리적인 방법이다.

    그러나 실버 불렛은 없는 법. Tahoe의 단점은 초반의 Slow Start 구간에서 윈도우 크기를 키울때 너무 오래 걸린다는 것이다. 물론 전체적으로 보았을 때 합 증가 방식보다는 지수 증가 방식이 빠르겠지만, 그래도 혼잡 상황이 발생했을 때 다시 윈도우 크기를 1부터 키워나가야 한다는 점은 어찌보면 낭비일 수도 있다.

    그래서 나온 방법이 빠른 회복(Fast Recovery) 방식을 활용한 TCP Reno이다.

    TCP Reno

    TCP Reno는 TCP Tahoe 이후에 나온 정책으로, Tahoe와 마찬가지로 Slow Start로 시작하여 임계점을 넘어서면 합 증가로 변경하는 방법이다.

    그러나 Tahoe는 명확한 차이가 있는데, 바로 3 ACK DuplicatedTimeout을 구분한다는 것이다. Reno는 3개의 중복 ACK가 발생했을 때, 윈도우 크기를 1로 줄이는 것이 아니라 AIMD처럼 반으로만 줄이고 sshthresh를 줄어든 윈도우 값으로 정하게 된다.

    reno ssthresh

    위 그래프는 앞서와 마찬가지로 청록색으로 송신 측의 혼잡 윈도우 크기를, 굵은 검정선으로 ssthresh를 표현하고 있다.

    Reno는 Tahoe와 다르게 3개의 중복 ACK가 발생했을 때는 혼잡 윈도우 크기를 1로 줄이는 것이 아니라 반으로 줄인 후 합 증가를 하게 되는데, 이 방식은 혼잡 윈도우 크기를 1로 줄이고 처음부터 다시 시작하는 Tahoe에 비해서 빠르게 원래 윈도우 크기에 도달할 수 있기 때문에 빠른 회복(Fast Recovery)라고 불린다.

    이때 ssthresh는 줄어든 윈도우의 크기와 동일하게 설정하게 된다. 위 그래프에서도 혼잡 상황이 발생하자 혼잡 윈도우 크기를 6에서 3으로 줄이고 ssthresh 또한 3으로 설정하는 모습을 볼 수 잇다.

    그러나 만약 타임아웃에 의해 데이터가 손실되면 Tahoe와 마찬가지로 윈도우 크기를 바로 1로 줄여버리고 Slow Start를 진행하고 이때는 ssthresh를 변경하지 않는다.

    즉, ACK가 중복된 상황과 타임아웃이 발생한 상황을 구분하면서 대처를 다르게 하고 있는 것이다. ACK 중복은 타임아웃에 비해 그리 큰 혼잡이 아니라고 가정하고 혼잡 윈도우 크기를 1로 줄이지도 않는다는 점에서 어느 정도 혼잡 상황에 경중을 따지고 있다는 것도 알 수 있다.

    마치며

    이번에는 TCP의 혼잡 제어 정책에 대해서 알아보았다. 사실 필자가 포스팅에서 소개한 Taheo와 Reno 같은 혼잡 제어 정책은 최근에는 많이 사용되지 않는 구식이다.

    예전에 개발된 Tahoe나 Reno 같은 경우는 말 그대로 예전의 네트워크 환경을 고려하며 설계된 친구들이라 최근의 대역폭 빵빵한 네트워크에는 다소 맞지 않는 것이 현실이다.

    cubic vs tahoe 높은 대역폭에서의 Tahoe와 CUBIC의 효율 차이를 보면 어마무시하다

    사실 Tahoe나 Reno가 개발된 시절의 네트워크 대역폭에 비교하면 최근의 네트워크 대역폭은 적어도 1,000배는 더 여유가 있지 않을까?

    그 말인 즉슨, 송신 측이 자신의 혼잡 윈도우 크기를 마음 놓고 팍팍 늘렸을 때 문제가 발생할 확률이 예전보다 많이 낮아졌다는 뜻이다. 그래서 최근의 혼잡 제어 정책들은 얼마나 더 빠르게 혼잡 윈도우 크기를 키우고, 어떻게 혼잡 감지를 더 똑똑하게 할 것이냐에 대해 초점이 맞춰져있다.


    3차 함수의 특성을 사용하는 TCP CUBIC은
    혼잡을 회피할 때는 거의 윈도우 크기를 증가시키지 않다가, 혼잡이 해결되면 다시 폭발적으로 증가시킬 수 있다


    그러나 필자가 굳이 예전 방식인 Tahoe와 Reno를 소개한 이유는 TCP 사용 초반에 개발된 혼잡 제어 방식인 만큼 원리가 간단하기도 하고, 이후 개발된 방식들도 큰 틀은 Tahoe와 Reno와 같은 매커니즘에서 크게 벗어나지 않기 때문이다.

    이 포스팅은 다양한 혼잡 제어 정책을 알아보는 것이 아니라, 혼잡 제어라는 매커니즘 자체가 어떤 식으로 흘러가는지 아는 것이 목표이므로 굳이 다른 혼잡 제어 정책은 소개하지 않았다.

    만약 최근에 사용하는 혼잡 제어 정책이 궁금하신 분들은 아래 첨부한 링크에서 조금 더 살펴보도록 하자.

    이상으로 사이 좋게 네트워크를 나눠 쓰는 방법, TCP의 혼잡 제어 포스팅을 마친다.

    Evan Moon

    🐢 거북이처럼 살자

    개발을 잘하기 위해서가 아닌 개발을 즐기기 위해 노력하는 개발자입니다. 사소한 생각 정리부터 튜토리얼, 삽질기 정도를 주로 끄적이고 있습니다.