사용자들이 너무 빠르게 전송하려 할때 오히려 전체 전송량이 줄어드는 현상을 막기위한 방법
위만 봐서는 뭔소린가 싶을테니 시나리오 별로 분석해보자
단일 홉을 공유하는 연결을 가진다고 해보자.
라우터를 공유하니 호스트 A와 B의 최대 전송률은 R/2가 될것이다.
만약 A와 B모두 전송률을 높여 R/2에 가깝게 전송한다면
무한 버퍼이니 큐잉 딜레이의 크기가 기하급수적으로 늘것이다.
위 상황에서 버퍼가 유한하다면 큐잉 딜레이가 커지다가
패킷들이 버려지게 될것이다.
송신자는 버려진 패킷에 대해서 다시 전송을 하게 된다.
이는 전체 전송률을 낮추게 된다.
-> 전체 보낸 패킷(수신자가 받은)/총 RTT가 전송률인데
수신자가 받는 패킷의 양은 그대로인데 재전송으로 인해 시간이 손실나기 때문이다.
위 사진과 같이 전송률이 커질수록 실제 데이터 throughput은 감소할것이다.
최악의 시나리오로 가정해보자
A->C, D->B를 보내다가 북쪽의 라우터가 막혔다.
이때 기가막히게 D->B로 보내는 패킷들만 손실이 나고 A->C는 무사히 통과했다고 해보자
잘 가다가 동쪽의 라우터에서 같은 현상이 벌어져서 이번에는 A에서 보낸 패킷만 막혔다.
이같은 현상이 계속 발생해서 동서남북 모든 라우터가 큐잉딜레이가 심하게 걸린다면
모든 라우터들은 재전송만 반복하게 될것이고
실제 throuhgput은 0에 수렴하게 된다.
각 사용자가 최선을 다하기 위해서 속도를 높였는데 결국 전체 시스템이 죽어버리는 현상이 발생한것이다.
이를 막기 위해서 혼잡제어가 필요하다.
혼잡제어는 네트워크에서 위같은 상황이 발생하면 얼른 풀어버린다.
라는 접근 방식이다.
전통적인 TCP의 혼잡제어를 알아보자
네트워크의 혼잡에 따라 연결에 트래픽을 보내는 전송률을 각 송신자가 제한하도록 한다.
TCP 송신자가 자신과 목적지 간의 경로에서 혼잡이 없음을 감지 → 송신율을 높인다.
TCP 송신자가 경로 사이에 혼잡을 감지 → 송신율을 줄인다.
이때 혼잡 윈도우를 통해 혼잡 제어를 하며 이 혼잡 윈도우 (cwnd)는
LastByteSent - LastByteAcked <= min{cwnd,rwnd} 값을 유지해야한다.
cwnd는 한번에 전송하는 패킷의 양을 조절하게 되므로 throughput = cwnd/RTT 가 될것이다.
손실이벤트가 발견되면 cwnd를 줄이게 되는데 손실이벤트는 다음과 같이 정의된다.
* 세그먼트가 손실이 나면 혼잡한거다.
즉,
1. ACK를 3번 연속 받거나
2. 타임아웃이 걸리거나
둘중 하나라면 TCP는 네트워크의 상태가 혼잡하다고 판단하고 cwnd를 조절한다.
TCP 연결이 시작되면 cwnd = 1MSS로 설정되며 빠르게 증가시킨다.
보낸 패킷에 대해 ACK응답이 하나 올때마다 cwnd를 하나씩 증가시키며
이는 cwnd를 지수적으로 증가시키는 결과를 가져온다.
이렇게 계속 보내다가 패킷이 손실이 난다면 cwnd =1 MSS로 다시 설정하고
ssthresh = cwnd/2 로 설정한다.
다시 슬로우 스타트 모드로 증가시키다가 cwnd == ssthresh 값이 된다면 혼잡 회피 상태로 들어가
조심스럽게 증가시키게 된다.
혼잡 회피에서는 매우 조심스럽게 cwnd를 증가시킨다.
앞에서는 한 패킷의 ACK당 하나씩 증가시켰다면
여기서는 모든 cwnd 양만큼의 ACK가 와야 1만큼 증가시킨다.
즉, cwnd = 4라면 ACK가 4개 와야 cwnd ++ 를 한다.
이렇게 계속 올리다가 또 혼잡 상태가 관측되면 ssthresh = cwnd/2로 바꾸고 다시 슬로우 스타트를 한다.
TCP 리노에서 사용되는 방법이다.
위 두 방법은 TCP 타호에서 사용하던 방법이고 리노는 빠른 회복 방법 또한 선택했다.
이 방법은 time-out이 아니라 3 ACK 가 왔을때 선택한다.
이 경우 그래도 timeout된거보다는 아직 괜찮다고 판단하여 혼잡상태가 발생했을때
ssthresh = cwnd/2로 cwnd = 손실시 cwnd/2 +3 으로 설정한다.
사진에서의 예를 보면 12에서 손실이 발생했고
타호는 바로 1로 가는 반면 리노는 9(6+3)으로 cwnd를 설정한후 혼잡회피 상태로 들어선다.
한기대 박기호 교수님의 설명으로는
TCP 리노에서 cwnd 설정후 손실난 패킷을 재전송하며 이때 정상적으로 재전송 된다면
다시 cwnd를 낮춰서 보낸다고 하는데
교재에서는 설명이 생략됬으니 나도 그냥 넘어간다.
혼잡제어에서 가법적 증가를 하고 손실 발생시 승법적 감소를 하여
AIMD라고 한다.
늘릴때는 조금씩 내릴때는 확
야 어차피 저기 손실난구간까지 가야 뭐 날거같은데 걍 저기까진 빨리올리고
근처 가면 천천히 상황보죠? 라는 의미
리노는 선형적으로 올렸지만 큐빅은 이전에 문제가 생긴구간까지 빠르게 증가시킨후
그다음부터 천천히 증가시키면서 더 효율적으로 사용하는것이다.
리눅스 운영체제에서 사용되는 버전이다. 웹서버의 50%가 큐빅 사용중이기도 하다.
TCP는 혼잡상황을 알지 못하고 종단에서 결과만 보고 알아차려야했다.
이게 별로라서 네트워크에서 혼잡이 생기면 라우터가 혼잡하다는 걸 직접 알려주게 하자
혼잡 해진다면 IP 데이터그램속 (라우터가 꺼내볼수있음) ECN 필드를 11로 킨다(2비트임)
수신자가 해당 ECN 비트를 보고 혼잡한 상태임을 알게되고
이를 다시 ECN 에코 비트를 설정하여 TCP ACK를 보낸다.
수신자 또한 혼잡 상태를 알게 되고 빠른 재전송을 사용하여 혼잡윈도 줄이고 CWR 비트를 1로 설정한다.
만약 A 호스트가 전송을 하고 있고 B는 이제막 들어왔다고 해보자
A는 cwnd가 커져있을테지만 B는 이제 1인데 이게 공평해지나???
요약하자면 증가할때는 비슷한 한계까지 증가하며
감소할때는 1/2로 감소 하는 행위를 반복하기에 결국 비슷비슷해진다.
UDP 에서는 공평? 그딴거 우리는 모른다 일단 빨리 갔다놓고 잃어버리거나 오류나면 뭐 알아서 하슈...
TCP는 연결하는데도 시간걸리고 TLS 연결도 또하고 하니까
UDP의 빠른 전송을 기반으로 어플리케이션 레이어에서 알아서 보안등을 처리하게 하자
하는게 QUIC에서는 보안 연결까지 한번에 연결하고 처리하기에 더욱 빠르다.