마지막 세그먼트가 손실됐을 때 - TLP와 RACK
Tail Loss에서는 중복 ACK를 기대하기 어렵다
송신자가 P0, P1, P2, P3을 차례로 보냈고 P1부터 P3까지 손실됐다고 가정해보겠습니다. 수신자는 P0을 받고 누적 ACK를 보냅니다. 그 뒤에는 수신할 세그먼트가 없으므로 ACK도 더 오지 않습니다.
시간 ↓
송신자 수신자
|---- P0 ------------------------------->| P0 수신
|---- P1 ---- X |
|---- P2 ---- X |
|---- P3 ---- X |
|<-------------------- P0까지 누적 ACK ---| 이후 피드백 없음
| |
| loss probe timeout(PTO) 만료 |
|---- P3 재전송(TLP) ------------------->| P3 수신, P1·P2는 비어 있음
|<-------------------------- SACK P3 ----|
| RACK이 P1·P2를 손실로 표시 |
| 혼잡 제어가 허용한 범위에서 재전송 |
|---- P1 재전송 ------------------------>|
|---- P2 재전송 ------------------------>|
|<-------------------- P3까지 누적 ACK ---| 연속 구간 복구
기존 방식만 사용하면 마지막으로 확인되지 않은 데이터는 RTO가 만료될 때까지 기다릴 가능성이 큽니다. 복구가 늦어질 뿐 아니라 RTO가 만료되면 혼잡 윈도우도 크게 줄어듭니다.
RFC 8985는 이 구간을 RACK-TLP로 다룹니다. TLP는 ACK 피드백을 얻기 위해 프로브(probe) 세그먼트 하나를 보내고, RACK은 돌아온 ACK나 SACK을 바탕으로 앞서 보낸 세그먼트의 손실을 판단합니다.
TLP는 ACK를 받기 위해 프로브를 보낸다
TLP(Tail Loss Probe)는 손실된 세그먼트를 모두 찾아 한꺼번에 재전송하는 방식이 아닙니다. ACK가 끊긴 상태에서 프로브 하나를 보내 수신자의 응답을 이끌어냅니다.
TLP는 **loss probe timeout(PTO)**이 만료되면 프로브를 보냅니다. SRTT가 있으면 기본 PTO는 2 × SRTT, 없으면 1초입니다. 전송 중인 세그먼트가 하나라면 지연 ACK를 고려한 시간을 더할 수 있고, RTO 만료 시점보다 늦게 잡지는 않습니다.
PTO가 만료되면 송신자는 다음 순서로 프로브를 고릅니다.
- 아직 보내지 않은 데이터가 있고 수신 윈도우가 허용하면 새 데이터 한 세그먼트를 보냅니다.
- 보낼 새 데이터가 없다면 지금까지 보낸 세그먼트 중 Sequence Number가 가장 높은 세그먼트를 재전송합니다.
새 데이터를 보내는 경우에는 수신자가 그 데이터에 대해 누적 ACK나 SACK을 돌려줍니다. 가장 높은 세그먼트를 재전송하는 경우에도 수신 결과가 ACK 피드백으로 돌아옵니다. 위의 예에서는 P3을 다시 보냈고 수신자가 P3 구간을 SACK으로 알렸습니다. 송신자는 이 피드백을 통해 P1, P2가 여전히 확인되지 않았다는 사실을 알 수 있습니다.
동시에 여러 프로브를 보내지 않으며, 혼잡 윈도우를 넘어 추가로 보낼 수 있는 프로브도 하나뿐입니다. 프로브나 그 ACK도 손실될 수 있으므로 RTO는 마지막 복구 수단으로 남습니다.
프로브가 손실을 바로 복구하는 경우
마지막 세그먼트 하나만 손실됐다면 가장 높은 세그먼트를 재전송한 프로브가 곧 복구 패킷이 됩니다.
P0 수신 → P1 수신 → P2 수신 → P3 손실
↓
TLP가 P3 재전송
↓
수신 구간이 이어지고 누적 ACK 진행
이 경우에는 SACK으로 다른 빈 구간을 찾고 추가 재전송을 할 필요가 없습니다. TLP가 다시 보낸 P3이 유일한 빈 구간을 채웁니다. 다만 ACK 모양만 보면 일반적인 누적 ACK처럼 보일 수 있으므로, 송신자는 TLP 재전송 상태를 따로 기록해 손실에 맞는 혼잡 제어가 적용되도록 처리합니다.
RACK은 전송 시각으로 손실을 판단한다
TLP가 피드백을 만드는 쪽이라면 RACK(Recent ACKnowledgment)은 그 피드백으로 손실을 판단하는 쪽입니다. 기존 방식이 중복 ACK의 수나 SACK된 시퀀스·바이트 양을 기준으로 삼았다면, RACK은 어느 세그먼트가 언제 전송됐고 그중 무엇이 도착했는지를 함께 봅니다.
송신자는 각 데이터 세그먼트의 가장 최근 전송 시각을 기록합니다. 재전송했다면 처음 보낸 시각이 아니라 가장 최근에 다시 보낸 시각으로 갱신합니다. ACK나 SACK이 도착하면 새로 전달됐다고 확인된 세그먼트 중 최근에 전송된 세그먼트를 기준으로 RTT를 구하고, 그보다 앞서 전송됐지만 아직 확인되지 않은 세그먼트를 확인합니다.
RACK의 판단 흐름은 다음과 같습니다.
더 늦게 보낸 세그먼트가 먼저 전달됐고,
이전 세그먼트가 전송 시각 + 최근 RTT + 재정렬 여유를 지나도록 확인되지 않았다면
이전 세그먼트를 손실로 판단한다.
여기서 재정렬 여유가 reordering window입니다. 뒤에 보낸 패킷이 먼저 도착했다고 해서 앞의 패킷이 반드시 손실된 것은 아닙니다. 서로 다른 경로를 지나거나 네트워크 내부에서 순서가 바뀌었을 수도 있습니다. 이 값은 연결 상태에 따라 0일 수도 있고, 재정렬이 관찰되면 일정 범위 안에서 늘어날 수도 있습니다. RACK은 이 여유를 반영해 재정렬과 손실을 구분합니다.
RACK은 세그먼트마다 별도 타이머를 두는 방식이 아닙니다. 최근 전송 시각을 기록하고 연결 단위 reordering timer를 사용합니다. 전송 시각은 송신자가 내부에 기록하므로 TCP Timestamp 옵션은 필수 조건이 아닙니다.
RACK-TLP를 쓰는 연결에서는 SACK을 사용하고, 송신자는 연결마다 SACK scoreboard를 유지합니다. RACK은 ACK와 SACK으로 확인한 범위에 최근 전송 시각을 더해, 먼저 보냈지만 아직 전달되지 않은 범위를 찾습니다.