TCP 통신에서 client와 server간의 packet loss가 발생하면 해당 data packet을 복구하기 위한 단계에 돌입한다. TCP packet 손실 복구 알고리즘은 여러가지가 있는데, 다음과 같다.
TCP Reno의 'fast recovery' 방식에는 두 가지 주요한 문제점이 있다.
1. 같은 packet들에 대한 여러 번의 congestion window(cwnd) 감소 -> TCP의 Reno 새 버전에서는 해결이됨
2. 같은 congesion window에서 유실 여러 개의 packet들을 복구하는데 너무 오랜 시간이 걸린다.
2번째 문제는 SACK을 통해서 해결이 가능하다. SACK을 간단히 말하자면 유실된 packet들을 빠르게 보내어 성능을 향상시키자는 것이다.
SACK은 sender, receiver 측의 TCP 최적화이므로, 둘 다 이를 제공해야지만 사용할 수 있다. 참고로, SACK은 기존의 cummulative ack 방식을 완전히 대체하는 것이 아니라, 그 위에서 하나의 option으로 동작한다. 실제로 TCP header의 option field로 SACK 정보가 들어있다. TCP SACK option은 40byte로 header가 2byte 정도된다.
SACK
주요 기능은 sender가 수신자의 receive buffer에서 어떤 packet들이 유실되었는 지에 대한 gap을 식별해준다. 즉, receiver가 packet들을 순서대로 받지 못하면 SACK
이 sender에게 어떤 부분의 packet들이 유실되었는 지 조기에 알려주도록 한다. 이때 sender가 어떤 packet들이 유실되었는 지를 알면, 유실된 packet들을 한 번에 쭉 보내준다. 이는 fast recovery와 달리 SACK은 sender에게 너무 늦게 손실된 packet을 알리지 않는다는 것이다.
sender의 cwnd=5이므로 packet 0,1,2,3,4를 한 번에 쭉 보낸다고 하자. receiver는 packet0을 받고 다음 sequence number인 ACK packet을 전달한다. 이를 ACK-1이라고 하자.
sender는 ACK-1
을 받고 packet-5
를 전달한다. receiver가 packet-2
을 받으면, packet-1
이 안왔으므로 ACK-1
을 전달한다. 단, SACK-2,3
도 함께 보낸다. 이는 순서가 어긋난다는 것을 말한다. sender는 ACK-1, SACK-2,3
을 받고 cwnd=5이므로 packet-6도 그대로 보낸다.
이때, SACK
의 의미를 잘보도록 하자. SACK
은 특정 구간을 나타낸다고 보면된다. 이 구간은 packet이 안전하게 왔다는 것을 나타내는 것이다. 즉, SACK-2,3
은 packet-2까지는 안전하게 잘 왔다는 것을 의미한다. packet-2에 대해서 응답으로 ACK-1
이 왔다는 것은 packet-1이 제대로 전달이 안되었다는 것을 말한다. 따라서, sender는 해당 정보를 보고 packet-0, packet-2는 제대로 갔고, packet-1이 유실되었구나!를 알게된다.
이후 packet-3이 유실된 상황을 보도록 하자.
receiver는 packet-4를 받고, 응답으로 ACK-1, SACK: 2-3, 4-5
를 보낸다. 이는 packet-1이 유실되었으므로 ACK-1인 것이고, packet-3이 유실되었기 때문에 SACK
구간으로 2-3
, 4-5
두개가 있는 것이다. 즉, packet-2와 packet-4는 정상적으로 왔지만, packet-3은 유실되었다는 것이다.
receiver는 packet-5와 packet-6를 정상 수신한다. 이는 정상적으로 packet을 받은 구간이 늘어났다는 것을 의미하므로, SACK
의 범위가 4-7
이 된다. 따라서 packet-6에 대한 응답이 ACK-1, SACK:2-3, 4-7
이 된다. 이는 packet0, packet2, packet4, packet5, packet6가 제대로 전달되었다는 것을 의미하고, packet1, packet3은 유실되었다는 것을 의미한다.
마침내, sender는 ACK-1을 3번 받았기 때문에 packet-1이 유실되었다는 사실을 안다. 이는 fast recovery 정책에 의해서 재전송이 이루어진다. 기존의 fast recovery 정책은 여기서 packet-1만 재전송하고 packet-3을 나중에 재전송한다. 이는 실제 TCP 통신에 있어서 엄청난 Throughput의 저하를 가져온다.
packet-1을 재전송하고도 packet-3을 또 재전송해야한다. 이는 데이터를 전송하는 sender 입장에서는 cwnd가 줄어들게 되고, retransmission 처리에 대한 오버헤드가 커진다. 데이터를 받는 receiver 입장에서는 유실된 데이터에 대한 순서 정렬을 위해 순서가 어그러진 packet들을 receive buffer에 넣고 대기한다. 이 과정에서 receiver의 buffer에 계속해서 packet이 들어가 처리가 되지 않고 있으므로 memory가 급격히 상승하여 process 동작에 영향을 주거나 host에 영향을 줄 수 있다. 또한, receive buffer가 줄어든다는 것은 packet을 받을 수 있는 window size가 감소한다는 의미로, 처리량(throughput)이 감소할 수 밖에 없다.
SACK은 이러한 문제를 해결해주는데, packet-3를 SACK의 구간을 보고 재전송해주는 것이다. 따라서 packet-1이 재전송되고 바로 packet-3도 재전송되는 것이다. 이전의 cummulative ack의 경우는 packet-1을 재전송하고, 또 다시 duplicate ack 3번을 받은 다음에 packet-3을 재전송했지만, SACK을 통해서 구간 내에 비어있는 packet들을 재전송하는 것이다. 정리하자면 SACK은 유실된 packet들의 구간을 알려주어 한 방에 전달해달라고 요청하도록 하는 것이다.
packet-3까지 전송이 된 후에 receiver는 receive buffer에 있던 packet들이 순서가 제대로 정립된다. 이제 모든 packet들이 순서대로 왔으니 ACK-7을 전달해 packet-7을 요청한다.
SACK의 구간의 시작을 SLE(SACK left edge)
라고 하고, 끝을 SRE(SACK right edge)
라고 한다. 즉, SACK: 2-3, 4-7
은 다음과 같이 풀어 쓸 수 있다.
구간1(SLE:2, SRE:3)
구간2(SLE:4, SRE:7)
SLE는 수신한 packet에 포함이고, SRE는 아니다. 정확히는 오기를 바라는 packet이라고 생각하면 된다.
만약 SACK: 30-40, 45-46
이라면 40~44까지의 모든 packet이 유실되었으므로 sender에게 한 번에 보내라고 한다. sender는 SACK을 보고 유실된 packet 구간을 알아차리며 packet-40,41,42,43,44를 한 번에 전송한다.
단, SLE와 SRE이 모두 Sequence Number이기 때문에 4byte를 차지한다는 것을 유념해야한다. 이는 한 개의 구간을 표현하는데 8byte가 필요하다는 것이고, TCP SACK option은 2byte이기 때문에 40byte로 이루어진 SACK field는 38byte에서 8byte씩인 총 4개의 SACK 구간만 표현이 가능하다.
따라서, 구간이 4개까지가 한계라는 것이다.
마지막으로 SACK은 오늘 날에 거의 모든 시스템에 설정되어있기 때문에 크게 걱정하지 않아도 되지만, 만약 설정되어있지 않다면 처리량(throughput)이 엄청나게 감소하게 된다. 이는 무선 네트워크 특성 상 packet 지연과 손실이 더 자주 발생하기 때문에 SACK을 통한 즉시 재전송이 성능을 크게 향상시키는 것이다.