요약

  • TCP의 누적 확인 응답은 강한 약속을 합니다. 이 숫자보다 낮은 모든 바이트가 도착했다는 것입니다. 세그먼트 하나가 사라지면, 뒤따르는 세그먼트들이 안전하게 대기 중이더라도 그 약속이 멈춥니다.
  • SACK는 연속되지 않는 바이트 범위를 협상을 통해 보고하는 기능을 추가했습니다. 일반 확인 응답 필드를 바꾸지 않고, 재전송 주도권을 수신자에게 넘기지 않으면서 송신자가 빈 공간을 찾도록 돕습니다.
  • 이 보고는 범위가 제한된 참고 정보입니다. 수신자가 약속을 번복하거나 옵션 정보가 사라지면, 송신자는 데이터를 보존하고 혼잡 규칙을 적용하며 누적 증거로 되돌아갑니다.

하나의 구멍 뒤에 놓인 일곱 세그먼트

수신자가 바이트 5000을 기다리고 있다고 가정해 봅시다. 5000에서 시작하는 세그먼트는 손실되었지만, 그 뒤의 일곱 세그먼트는 도착하여 5500부터 9000까지의 범위를 채웠습니다. 일반 확인 응답은 여전히 5000을 말합니다. 이는 무지도 아니고 오류도 아닙니다. 앞쪽 필수 바이트가 없는 한 끊기지 않은 스트림을 증명하기를 정확하게 거부하는 것입니다.

그러나 송신자 입장에서는 같은 숫자가 일곱 번 반복되는 것은 쓸모없는 지도입니다. 5000 이후가 전부 사라졌는지, 첫 세그먼트만 빠졌는지, 패킷 순서가 바뀌었는지 말해 주지 않습니다. 전체 비행을 다시 보내면 용량이 낭비되고, 구멍을 한 왕복에 하나씩 찾으면 시간이 낭비됩니다. TCP 창이 커질수록 한 번의 비행 안에서 여러 손실이 일어나는 상황은 이 모호성 때문에 점점 더 큰 비용을 치르게 되었습니다.

원래 TCP 규격은 누적 규칙을 명시했습니다. 모든 옥텟에 순서 번호가 붙고, 확인 응답X는X미만의 모든 옥텟이 수신되었음을 뜻합니다. 이 압축된 약속 덕분에 버퍼를 최종적으로 해제할 수 있고 스트림이 앞으로 나아갈 수 있습니다. 그 강함이 곧 맹점을 만드는 데, 수신자는 뒤의 데이터를 가지고 있더라도 왼쪽 경계를 구멍 너머로 밀어갈 수 없습니다.

구현 합의 이전에 나온 옵션

1988년 10월, RFC 1072는 기본 필드를 재정의하지 않고 더 많은 정보를 담는 방법을 제안했습니다. 연결 시작 SYN에 담긴SACK-Permitted옵션이 이 기능을 협상합니다. 이후의 확인 응답은 SACK 옵션을 담아 이미 수신되어 대기 중인 연속되지 않는 블록들을 기술할 수 있습니다.

이 설계는 명세된 그대로 배포되지 않았습니다. RFC 2018은 이후 이것이 창 이동(window-shift) 옵션과의 상호작용에 대한 이견을 기록했습니다. 이것은 표준 역사에서 중요한 한 조각입니다. 문서의 공개는 후보 문법을 제공했을 뿐, 실제 시스템들이 수렴했다는 증거는 아니었습니다. 첫 설계는 개정할 가치가 충분히 남았지만, 문서의 날짜가 종단점을 업그레이드해 주지는 않았습니다.

1996년 개정판은 두 옵션 구조를 유지하면서 표현 방식을 바꿨습니다. 각 블록이 이제 왼쪽과 오른쪽 경계를 모두 32비트 전체로 담습니다. 왼쪽 경계는 존재하는 첫 바이트이고, 오른쪽 경계는 마지막 바이트 바로 다음 순서 번호입니다. 예제에서 누적 확인 응답은 5000으로 남아 있으면서 SACK 블록은[5500, 9000)을 보고할 수 있습니다. 낡은 약속과 새로운 관찰이 한 패킷 안에 공존합니다.

수신자 전체를 담기에는 너무 작은 지도

TCP 헤더는 옵션에 40바이트만 남깁니다. SACK 보고는 종류와 길이에 2바이트, 블록당 8바이트를 사용하므로 혼자서는 최대 네 블록까지 나열할 수 있습니다. 타임스탬프를 함께 쓰면 실질적인 최대치는 흔히 세 블록입니다. 조각난 수신 큐에는 패킷 하나가 기술할 수 있는 것보다 더 많은 섬이 있을 수 있습니다.

그래서 RFC 2018은 가장 새로 도착한 데이터가 바꾼 블록을 우선 보고하고, 최근 블록들은 이후의 확인 응답에서 반복합니다. 중복이 중요한 이유는 확인 응답 자체도 손실될 수 있기 때문입니다. 수신자는 완전하고 영구적인 장부를 공표하지 않습니다. 송신자가 더 쓸모 있는 그림을 쌓아 갈 수 있는, 범위가 제한된 굴러가는 스케치를 보낼 뿐입니다.

이것은 아키텍처적 절약이었습니다. 유선 형식은 수신자의 전체 큐, 구현 방식이나 메모리 정책을 밖으로 내보내지 않고도 복구를 개선할 만큼의 공유 증거만을 담았습니다. 서로 다른 종단점은 두 개의 순서 경계에 합의하면서 서로 다른 내부 구조를 유지할 수 있었습니다.

조언은 최종 인도가 아니다

규격은 SACK를 참고(advisory) 정보로 부릅니다. 수신자는 이전에 보고했던 바이트를 나중에 버릴 수도 있는데, 이런 행위를 reneging(약속 번복)이라고 부릅니다. 그래서 송신자는 범위를 선택적으로 확인된 것으로 표시하고 일반 복구에서 건너뛸 수 있지만, SACK 블록이 언급했다는 이유만으로 원본 데이터를 해제해서는 안 됩니다. 그 의무를 닫는 것은 누적 확인 응답의 전진뿐입니다.

재전송 타임아웃이 발생한 뒤에는, 송신자가 이전의 SACK 상태를 무시하고 왼쪽 경계부터 다시 재전송할 준비가 되어 있어야 합니다. 이 대체 동작은 보수적으로 보이지만 결정적인 구분을 지켜 줍니다. 일시적인 관찰은 행동을 고르는 데 도움이 될 뿐, 번복 불가능한 수령 증명이 되지는 않습니다.

이 구분은 권한도 제한합니다. 수신자는 자신의 큐를 기술할 수 있지만 재전송 명령을 내리지는 못합니다. 재전송 큐를 유지하고 보고를 결합하고 구멍을 추론하고 다음에 보낼 수 있는 세그먼트를 결정하는 것은 송신자입니다. RFC 6675는 이후 그 송신자 측 상태를 스코어보드라고 부르고, 네트워크에 남은 데이터 양을 추정하는 보수적인 알고리즘을 제시했습니다.

더 좋은 증거가 더 큰 통행량을 사다 주지는 않았다

SACK는 혼잡 권한이 아니라 손실 복구를 개선했습니다. RFC 2018은 이미 확립된 혼잡 제어 동작이 계속 효력을 유지할 것을 명시했습니다. 순서가 바뀐 보고 하나가 손실을 증명하지는 않으며, 송신자는 여전히 적절한 임계값을 필요로 합니다. 복구 중에는 새 전송을 제한하고 혼잡 증거가 요구하면 혼잡 창을 줄입니다.

이 경계가 이 역사를 1986년 혼잡 붕괴 수리의 역사와 나눕니다. 느린 시작과 혼잡 회피는 송신자가 네트워크에 얼마나 넣을 수 있는지를 결정합니다. SACK는 그 허용량 안에서 어떤 바이트를 보낼 가치가 있는지 결정하도록 돕습니다. 더 정확한 지도가 더 넓은 도로는 아닙니다.

2000년에 D-SACK는 보고 범위를 중복 데이터까지 확장했습니다. 수신자는 두 번 이상 받은 바이트 범위를 식별할 수 있게 되었고, 송신자는 이를 통해 재정렬, 잃어버린 확인 응답, 복제 혹은 조급한 타임아웃을 추론할 수 있었습니다. 문서는 의도적으로 하나의 반응을 규정하지 않았습니다. 또한 송신자가 수신자의 보고를 반드시 신뢰할 수는 없다고 경고했습니다. 새 증거는 더 좋은 질문을 만들었을 뿐, 명령에 복종하는 액추에이터를 만들지는 않았습니다.

출처와 증거의 한계

누적 확인 응답의 기본 의미는RFC 793에서 나옵니다. 첫 옵션 설계는RFC 1072이고, 배포된 표준 트랙 형식과 그 한계는RFC 2018에 있습니다. 중복 보고는RFC 2883이 정의하고, 혼잡 경계는RFC 5681이, 낡은 제안의 이후 지위는RFC 6247이, 보수적인 스코어보드 복구 알고리즘은RFC 6675가 다룹니다.

이 문서들은 프로토콜 정의, 계보, 명시된 한계를 증명합니다. 그러나 전 세계적 활성화 날짜, 보편적인 성능 향상, 특정 패킷 손실의 위치와 원인을 확립하지는 않습니다. SACK는 한 순간에 수신자가 주장하는 상태를 보고할 뿐이며, 인증도 아니고 내구성 있는 인도 증명도 아닙니다.