업데이트됨 1개월 전
소방호스로 물을 마시려는 상황을 상상해보세요. 혼잡한 Wi-Fi 환경에서 기가비트 서버가 스마트폰에 아무런 조율 없이 데이터를 보낼 때 바로 그런 일이 벌어집니다. 스마트폰의 버퍼가 넘쳐흐르고, 패킷이 손실되며, 양쪽 모두 재전송에 시간을 낭비하게 됩니다.
TCP의 슬라이딩 윈도우 프로토콜은 이를 방지합니다. 수신자가 "지금 내가 처리할 수 있는 양은 이만큼입니다"라고 지속적으로 알릴 수 있는 간단한 방법을 제공합니다. 송신자는 반드시 그 수치를 지켜야 합니다.
핵심 개념: 수신 윈도우 통보
수신자가 보내는 모든 TCP 세그먼트에는 16비트 윈도우 필드가 포함되어 있습니다. 수신 윈도우(rwnd)라고 불리는 이 값은 송신자에게 현재 사용 가능한 버퍼 공간이 정확히 몇 바이트인지 알려줍니다. 송신자는 이미 확인 응답된 데이터를 기준으로 최대 그 바이트 수만큼만 더 전송할 수 있습니다.
수신자의 애플리케이션이 버퍼에서 데이터를 읽으면 공간이 생깁니다. 다음 확인 응답은 더 큰 윈도우를 알립니다. 처리되지 않은 데이터가 쌓이면 윈도우는 줄어듭니다. 이를 통해 지속적인 피드백 루프가 형성됩니다. 송신자는 언제나 수신자의 현재 처리 용량을 파악할 수 있습니다.
윈도우가 슬라이딩하는 방식
슬라이딩 윈도우에서 "슬라이딩"이란 데이터가 흐름에 따라 전송 경계가 앞으로 이동하는 방식을 의미합니다.
수신자의 버퍼가 64KB이고 rwnd=65536을 알리는 경우를 생각해봅시다. 송신자가 32KB를 전송합니다. 수신자의 애플리케이션이 16KB를 처리하고, 버퍼에 아직 16KB가 남아 있으므로 rwnd=49152(64KB에서 16KB를 뺀 값)로 확인 응답을 보냅니다. 이제 송신자는 추가로 16KB를 전송할 수 있습니다.
확인 응답이 도착하고 데이터가 처리되면서 윈도우는 앞으로 슬라이딩합니다. 여러 세그먼트가 동시에 전송 중 상태로 유지됩니다. 하나의 세그먼트를 보내고 확인 응답을 기다린 후 다음을 보내는 방식보다 훨씬 효율적입니다.
64KB 한계와 윈도우 스케일링
16비트 윈도우 필드로 인해 최대 65,535바이트라는 엄격한 제한이 생깁니다. 1981년에는 충분한 크기였지만, 현대 네트워크에서는 심각한 족쇄가 됩니다.
왕복 지연 시간이 100밀리초인 연결을 생각해보세요. 64KB 윈도우에서는 왕복당 최대 64KB만 전송할 수 있습니다. 실제 연결 속도가 아무리 빨라도 약 5Mbps가 한계입니다. 프로토콜이 파이프를 가득 채우지 못하게 가로막고 있기 때문에 대역폭이 그대로 낭비됩니다.
윈도우 스케일링(RFC 1323)은 핸드셰이크 중에 배율을 협상하는 방식으로 이를 해결합니다. 양측이 스케일 팩터(0~14)를 교환하고, 이후의 모든 윈도우 값은 그 값만큼 왼쪽으로 비트 시프트됩니다. 스케일 팩터가 7이면 윈도우 크기가 128배가 됩니다. 65,535바이트의 한계가 단번에 8MB로 늘어나 빠른 링크도 충분히 채울 수 있게 됩니다.
단, 윈도우 스케일링은 초기 3방향 핸드셰이크 중에 협상해야 합니다. 연결 도중에는 활성화할 수 없으며, 양측 모두 이를 지원해야 합니다.
제로 윈도우와 교착 상태 문제
때로는 수신자의 버퍼가 완전히 가득 차기도 합니다. 애플리케이션이 빠르게 읽지 못하고 새 데이터를 위한 공간이 없습니다. 수신자는 rwnd=0을 알립니다. 전송을 멈추세요.
이는 흐름 제어가 설계된 대로 정확히 작동하는 모습입니다. 그런데 여기서 미묘한 문제가 생깁니다.
수신자의 애플리케이션이 데이터를 따라잡고 버퍼 공간이 생깁니다. 수신자는 윈도우 업데이트를 보냅니다. rwnd=16384. 그런데 그 패킷이 손실됩니다. 이제 양쪽 모두 기다리는 상황이 됩니다. 송신자는 0이 아닌 윈도우를 기다리고, 수신자는 결코 오지 않을 데이터를 기다립니다. 교착 상태입니다.
TCP는 윈도우 프로브로 이를 방지합니다. 송신자가 rwnd=0을 확인하면 지속 타이머를 시작합니다. 타이머가 만료되면 응답을 강제하기 위해 작은 프로브, 즉 1바이트의 데이터를 전송합니다. 수신자는 프로브를 확인 응답하고 현재 윈도우를 다시 알립니다. 여전히 0이면 송신자는 점점 간격을 늘려가며 나중에 다시 프로브를 보냅니다. 공간이 생겼다면 정상 전송이 재개됩니다.
프로브는 TCP가 침묵을 깨기 위해 1바이트로 "거기 계세요? 아직 있나요?"라고 묻는 것과 같습니다.
흐름 제어와 혼잡 제어
비슷하게 들리지만 서로 다른 문제를 해결합니다.
흐름 제어는 수신자 주도입니다. 수신자가 윈도우 크기를 통보함으로써 자신의 처리 용량을 송신자에게 명시적으로 알립니다. 이 제약은 수신자 측에 있습니다. 이 특정 수신자가 데이터를 얼마나 빠르게 처리할 수 있는가?
혼잡 제어는 송신자 주도입니다. 송신자는 패킷 손실과 지연 시간 급증을 감지하여 네트워크 용량을 추론합니다. 이 제약은 경로에 있습니다. 두 엔드포인트 사이의 네트워크가 얼마나 많은 데이터를 감당할 수 있는가?
TCP는 두 가지를 동시에 사용합니다. 송신자의 실제 전송 한계는 수신자의 수신 윈도우(rwnd)와 송신자의 혼잡 윈도우(cwnd) 중 더 작은 값입니다. 연결이 느린 이유가 rwnd가 작기 때문이라면 수신자 측 문제입니다. 애플리케이션이 충분히 빠르게 읽지 못하거나 윈도우 스케일링이 필요한 것입니다. cwnd가 병목이라면 네트워크가 혼잡한 것입니다.
문제가 다르면 해결책도 다릅니다. 어느 것이 연결을 제한하는지 파악하는 것이 문제 해결의 첫 번째 단계입니다.
TCP 흐름 제어에 관한 자주 묻는 질문
윈도우 스케일링이 지원되지 않으면 어떻게 되나요?
연결은 작동하지만 64KB 윈도우로 제한됩니다. 지연 시간이 높은 링크에서는 대역폭에 관계없이 처리량이 제한됩니다. 대부분의 현대 시스템은 윈도우 스케일링을 지원하지만, 레거시 장치나 TCP 옵션을 제거하는 미들박스로 인해 비활성화될 수 있습니다.
송신자가 수신 윈도우를 무시할 수 있나요?
아닙니다. 수신 윈도우는 절대적인 한계입니다. 통보된 윈도우를 초과하여 전송하는 송신자는 프로토콜을 위반하는 것입니다. 수신자는 해당 데이터를 버릴 권리가 있으며, 연결이 끊어질 수도 있습니다.
흐름 제어가 연결을 제한하고 있는지 어떻게 알 수 있나요?
패킷 캡처에서 작은 수신 윈도우가 있는지 확인해보세요. 네트워크에 사용 가능한 대역폭이 있음에도 rwnd가 지속적으로 낮게 유지된다면, 수신자의 애플리케이션이 처리를 따라가지 못하는 것입니다. rwnd는 크지만 처리량이 여전히 낮다면, 혼잡 제어나 네트워크 문제가 더 유력한 원인일 가능성이 높습니다.
이 페이지가 도움이 되었나요?