업데이트됨 1개월 전
TCP의 설계자들은 미래를 예측할 수 없다는 걸 알고 있었습니다. 기가비트 이더넷이나 위성 링크, 혹은 불과 몇 미터 거리에 있으면서도 테라바이트의 데이터를 주고받아야 하는 데이터 센터 같은 것들은 상상조차 하지 못했죠. 그래서 그들은 탈출구를 만들었습니다. 바로 TCP 옵션입니다. 이 선택적 헤더 필드 덕분에 양쪽 엔드포인트는 원래 명세를 넘어서는 기능을 협상할 수 있습니다. ARPANET을 위해 태어난 프로토콜이 설계자들이 꿈에도 그리지 못한 네트워크에서도 살아남을 수 있는 이유입니다.
옵션 필드
TCP 옵션은 표준 필드와 페이로드 사이의 헤더 안에 자리잡고 있습니다. 각 옵션은 자신의 종류와 길이를 선언하며, 이 필드는 최대 40바이트를 담을 수 있습니다. 핵심적인 협상은 3방향 핸드셰이크 과정에서 이루어집니다. 양쪽이 자신이 지원하는 기능을 알리고, 공통점을 찾아 합의하며, 그 매개변수를 연결이 유지되는 동안 지키기로 약속합니다.
한쪽이 어떤 옵션을 인식하지 못하면 그냥 무시합니다. 이 단순한 규칙이 TCP를 살아있는 프로토콜로 유지시켜 줍니다. 새로운 기능은 점진적으로 배포할 수 있고, 예전 구현 방식은 망가지지 않습니다. 혜택을 누리지 못할 뿐입니다.
최대 세그먼트 크기(MSS)
MSS는 실용적인 질문에 답합니다. TCP 세그먼트가 얼마나 크면 문제가 생길까요?
문제는 IP 단편화입니다. 패킷이 네트워크 링크의 최대 전송 단위(MTU)를 초과하면 라우터가 이를 여러 조각으로 나눕니다. 이는 성능을 저하시킵니다. 어느 한 조각이라도 손실되면 전체 패킷을 재전송해야 합니다. 더 나쁜 경우, 많은 방화벽은 단편화된 패킷을 의심스럽다고 여겨 아예 버려버립니다.
MSS는 자신이 받을 의향이 있는 가장 큰 세그먼트를 알림으로써 이 문제를 미리 막습니다. 계산은 간단합니다. 인터페이스의 MTU(이더넷은 보통 1500바이트)에서 IP 헤더 20바이트와 TCP 헤더 20바이트를 빼면 1460바이트가 됩니다. 각 엔드포인트는 핸드셰이크 중에 자신의 MSS를 알리며, 방향마다 다를 수 있습니다. 수신자가 알려주는 값을 존중하는 것이 원칙입니다.
경로 MTU 탐색(Path MTU Discovery)은 전체 경로를 탐색함으로써 이를 확장합니다. 라우터가 너무 큰 패킷을 만나면 ICMP "단편화 필요(Fragmentation Needed)" 메시지를 돌려보냅니다. 송신자는 세그먼트를 줄이고 다시 시도합니다. VPN 터널이나 PPPoE 연결을 통과하는 경로에서 특히 중요한데, 이런 경우 실제 MTU가 이더넷의 표준 1500바이트보다 작기 때문입니다.
윈도우 스케일링
TCP 수신 윈도우 필드는 16비트입니다. 최댓값은 65,535바이트입니다.
1981년에는 충분하고도 남아 보였습니다. 응답을 받기 전에 64KB 이상의 데이터를 보내야 할 거라고는 아무도 상상하지 못했죠. 하지만 오늘날에는 완전한 병목이 되고 말았습니다.
대역폭-지연 곱은 "전송 중"인 데이터, 즉 전송되었지만 아직 확인응답을 받지 못한 데이터의 양을 결정합니다. 왕복 시간이 100ms이고 대역폭이 1 Gbps인 대서양 횡단 연결이라면 이론적으로 12.5메가바이트가 전송 중 상태일 수 있습니다. 그런데 윈도우가 64KB라면 어떨까요? 64KB를 보내고 기다립니다. 또 64KB를 보내고 또 기다립니다. 기가비트 대서양 횡단 링크에서 64킬로바이트짜리 윈도우는 빨대로 수영장을 채우려는 것과 같습니다. 더 빠르게 퍼낼 수는 있지만, 물방울 하나하나가 잘 도착했는지 확인이 올 때까지 기다리고 있는 꼴입니다.
윈도우 스케일링은 배수를 사용해 이 문제를 해결합니다. 핸드셰이크 중에 양쪽은 0에서 14 사이의 시프트 카운트를 알립니다. 윈도우 값은 그 수만큼 왼쪽으로 비트 이동됩니다. 시프트 7은 128배가 되어 최대 8메가바이트의 윈도우를 허용합니다. 시프트 14는 1기가바이트 이상을 허용합니다.
이는 초기 핸드셰이크 중에 반드시 협상되어야 합니다. 나중에 활성화할 수 없습니다. 양쪽 모두 지원해야 합니다. 하지만 일단 활성화되면, TCP는 드디어 높은 대역폭과 높은 지연 시간의 링크를 가득 채울 수 있게 됩니다.
TCP 타임스탬프
타임스탬프는 두 가지 문제를 해결하는데, 두 번째가 예상보다 훨씬 흥미롭습니다.
첫 번째는 더 정확한 왕복 시간 측정입니다. 타임스탬프가 없으면 TCP는 ACK 타이밍으로 RTT를 추정하는데, 재전송 중에는 모호해집니다. ACK가 원래 세그먼트에 대한 응답인지, 재전송에 대한 응답인지 알 수 없습니다. 타임스탬프를 사용하면 모든 세그먼트에 송신자 타임스탬프와 수신자의 마지막 타임스탬프 에코가 담깁니다. RTT 계산이 명확해집니다.
두 번째는 시퀀스 번호 순환(wraparound) 방지입니다. TCP 시퀀스 번호는 32비트로, 4기가바이트 이후에 순환합니다. 10 Gbps 연결에서는 4초도 안 되어 전체 시퀀스 공간이 소진됩니다. 타임스탬프가 없으면 TCP가 오래된 패킷을 새 데이터로 착각할 수 있습니다. 이전 순환에서 온 세그먼트가 늦게 도착해 유효한 것으로 받아들여질 수도 있습니다. 타임스탬프가 보조 식별자 역할을 하여, 정당한 세그먼트와 이전 주기에서 뒤늦게 도착한 중복 세그먼트를 구별할 수 있게 합니다.
선택적 확인응답(SACK)
전통적인 TCP 확인응답은 누적 방식입니다. "5000바이트까지 모두 받았습니다." 패킷이 순서 없이 도착하거나 빈틈이 생기면 문제가 됩니다.
세그먼트 1, 2, 4, 5는 도착했지만 3은 손실된 경우를 생각해보세요. 수신자는 세그먼트 2까지만 확인응답을 보낼 수 있습니다. 송신자는 뭔가 잘못됐다는 건 알지만, 정확히 무엇이 문제인지는 모릅니다. 3, 4, 5가 모두 사라진 건지? 3만 없는 건지? 더 많은 정보 없이는 모든 것을 재전송하거나(낭비), 기다리며 탐색할 수밖에 없습니다(느림).
SACK은 수신자가 정확히 무엇을 받았는지 알릴 수 있게 합니다. "1-2는 누적으로 받았고, 4-5도 갖고 있습니다." 그러면 송신자는 세그먼트 3만 재전송합니다. 복구가 추측이 아닌 정밀한 수술이 됩니다.
SACK은 핸드셰이크 중에 SACK-Permitted 옵션으로 협상해야 합니다. 활성화되면 수신자는 최대 서너 개의 불연속 범위를 지정하는 SACK 블록을 포함할 수 있습니다. 무선이나 혼잡한 경로 등 패킷 손실이 있는 네트워크에서 SACK은 그럭저럭 쓸 만한 성능과 고통스러운 성능 사이의 차이를 만들어냅니다.
협상의 춤
옵션 협상은 정해진 순서를 따릅니다.
- SYN: 클라이언트가 선호하는 매개변수와 함께 지원하는 옵션을 알립니다(MSS, 윈도우 스케일 시프트 카운트, SACK-Permitted, 타임스탬프).
- SYN-ACK: 서버가 공통 옵션에 대한 자신의 매개변수로 응답합니다.
- ACK: 협상된 매개변수를 확인합니다.
일부 옵션은 방향마다 다를 수 있습니다. 각 쪽이 자신의 MSS를 가집니다. 다른 옵션들은 상호 지원이 필요합니다. 윈도우 스케일링과 SACK은 양쪽이 동의할 때만 활성화됩니다. 타임스탬프는 유연합니다. 기술적으로 한쪽만 사용할 수도 있지만, 보통 양쪽이 함께 활성화합니다.
핸드셰이크가 완료되면 매개변수는 고정됩니다. 연결 중간에 옵션을 추가할 수 없습니다. 이 때문에 초기 협상이 매우 중요하며, 옵션을 제거해버리는 미들박스가 미묘하고 골치 아픈 성능 문제를 일으키는 이유이기도 합니다.
핵심 정리
- TCP 옵션은 1970년대 프로토콜이 기가비트 세계에서 살아남을 수 있게 해주는 탈출구입니다. 하위 호환성을 유지하며 점진적으로 배포할 수 있고, 연결별로 협상됩니다.
- MSS는 최대 세그먼트 크기를 알림으로써 IP 단편화를 방지하며, 경로 MTU 탐색은 이를 전체 네트워크 경로 탐색으로 확장합니다.
- 윈도우 스케일링은 64KB 한계를 깨뜨려 메가바이트 단위의 데이터를 전송 중으로 유지함으로써 고지연 링크에서 높은 처리량을 가능하게 합니다.
- 타임스탬프는 RTT 측정을 개선하고 시퀀스 번호 순환을 방지합니다. 연결이 수 초 안에 4GB의 시퀀스 공간을 소모할 수 있는 상황에서 필수적입니다.
- SACK은 손실 복구를 추측에서 정밀도로 전환하여 송신자에게 정확히 어떤 세그먼트를 재전송해야 하는지 알려줍니다.
- 옵션은 핸드셰이크 중에 한 번 협상되어 연결 기간 동안 고정됩니다. 이 초기 교환이 성능에 조용히 결정적인 역할을 합니다.
TCP 옵션에 관해 자주 묻는 질문
연결이 수립된 후에는 TCP 옵션을 변경할 수 없는 이유가 무엇인가요?
TCP 옵션은 윈도우 크기 계산, 확인응답 의미론, 타임스탬프 해석 방식 등 연결의 근본적인 매개변수에 영향을 미칩니다. 이를 전송 중간에 변경하려면 양쪽이 스트림의 정확히 같은 바이트 위치에서 동시에 변경을 동기화해야 하는데, 이는 극히 복잡하고 오류가 생기기 쉽습니다. 핸드셰이크는 양쪽이 명시적으로 조율하는 유일한 순간이기 때문에, 매개변수를 고정하기에 자연스러운 지점입니다.
미들박스가 TCP 옵션을 제거하면 어떻게 되나요?
성능이 저하되는데, 흔히 원인을 알 수 없는 방식으로 나타납니다. 윈도우 스케일링이 없으면 고지연 링크에서 처리량이 최대 용량의 일부로 뚝 떨어집니다. SACK이 없으면 손실 복구가 느리고 낭비적이 됩니다. MSS 협상이 없으면 IP 단편화가 발생할 수 있습니다. 연결 자체는 여전히 동작합니다. TCP의 하위 호환성이 이를 보장합니다. 하지만 현대 네트워크를 견딜 만하게 만들어주는 최적화들을 잃게 됩니다.
모든 최신 시스템이 이러한 옵션을 지원하나요?
그렇습니다. MSS, 윈도우 스케일링, 타임스탬프, SACK은 수십 년째 표준입니다. 지난 20년간 출시된 운영체제라면 기본으로 모두 활성화되어 있습니다. 문제는 엔드포인트가 지원하느냐가 아니라, 그 사이의 네트워크 경로가 옵션들을 온전히 통과시키느냐입니다.
이 페이지가 도움이 되었나요?