윈도 스케일링
고친 사람 github-actions[bot]
윈도 스케일링은 데이터를 나르는 연결이 한 번에 받겠다고 알릴 수 있는 양의 상한을 넓혀 줍니다. 그 양을 적는 칸이 작아서 원래는 6만 5천 바이트 남짓이 끝입니다. 연결을 맺을 때 양쪽이 곱할 배율을 미리 정합니다. 그 뒤로 오가는 값은 배율을 곱해 읽으므로 상한이 10억 바이트 남짓까지 커집니다.
쉽고 빠른 이해
무슨 일을 하는 물건인가 — 받는 쪽이 「이만큼 더 받을 수 있다」고 알리는 숫자를 크게 읽게 해 줍니다. 칸에 1,000 이 적혀 있어도 배율이 128 이면 128,000바이트로 읽습니다.
왜 이렇게 하나 — 보내는 쪽은 답을 기다리는 동안 그 숫자만큼만 미리 보낼 수 있습니다. 숫자의 상한이 작으면 답이 오가는 동안 회선이 놉니다. 서울과 미국 서버처럼 오가는 데 오래 걸리는 연결일수록 손해가 큽니다.
어떻게 도나
- 연결을 여는 첫 신호에 양쪽이 각자 쓸 배율을 실어 보냅니다
- 양쪽이 다 보냈을 때만 켜지고, 한쪽이라도 안 보내면 꺼집니다
- 그 뒤로는 칸에 적힌 값에 배율을 곱해 읽습니다
대가 — 배율이 클수록 작은 차이를 적지 못해 숫자가 거칠어집니다. 이 약속을 모르는 중간 장비가 끼면 숫자를 잘못 읽어 연결이 느려지거나 끊깁니다.
상세
이 절은 TCP(Transmission Control Protocol, 전송 제어 프로토콜)가 「더 받을 수 있는 양」을 적는 칸이 왜 모자라게 됐는지부터 봅니다. 그다음 배율을 정하는 방법과 그 배율이 연결을 맺는 순간 어떻게 오가는지를 두 컴퓨터의 대화로 따라갑니다.
끝에서는 배율 때문에 생기는 대가 둘을 짚습니다. 숫자가 거칠어지는 것, 그리고 배율을 모르는 중간 장비가 숫자를 잘못 읽는 것입니다.
가격표에 숫자 칸이 다섯 개뿐인 가게를 떠올려 보세요. 99,999원보다 비싼 물건은 값을 적을 수가 없습니다. 그래서 가게 주인과 손님이 미리 「이 가게 가격표는 만 원 단위다」라고 정해 둡니다. 그러면 같은 다섯 칸으로 10억 원 가까이 적을 수 있습니다.
6만 5천 바이트에서 멈추는 윈도 필드
TCP 는 데이터를 잘게 나눠 세그먼트라는 묶음으로 보냅니다. 세그먼트마다 앞머리에 헤더가 붙습니다. 헤더는 받는 쪽이 이 묶음을 어떻게 다룰지 적어 둔 정보 칸들의 모음입니다.
그 칸 가운데 하나가 윈도 필드입니다. 받는 쪽이 「지금 이만큼 더 받을 수 있다」를 바이트 수로 적어 보내는 칸입니다. 칸 자체는 윈도 필드, 그 칸에 적힌 수는 윈도라고 부릅니다.
보내는 쪽은 답을 못 받은 데이터가 윈도를 넘지 않게 맞춥니다. 받는 쪽이 감당 못 할 만큼 몰려와 데이터를 버리는 일을 막으려는 것입니다. 이렇게 받는 쪽 형편에 맞춰 보내는 양을 조절하는 일을 흐름 제어라고 합니다.
윈도 필드의 폭은 16비트입니다. 16비트로 적을 수 있는 가장 큰 수는 65,535 입니다. 그래서 배율 없이는 받는 쪽이 아무리 메모리를 넉넉히 잡아도 65,535바이트, 약 64킬로바이트보다 더 받겠다고 알릴 수 없습니다.
상한이 처리량을 막는 연결
윈도가 작으면 보내는 쪽은 윈도만큼 보낸 뒤 답이 올 때까지 기다립니다. 답이 돌아오는 데 걸리는 시간을 왕복 시간이라고 합니다. 그러니 한 왕복에 윈도만큼밖에 못 보냅니다.
왕복 시간이 0.1초인 연결에 이 상한을 대 보면 다음과 같습니다. 회선이 초당 1기가비트를 낼 수 있어도 결과는 같습니다. 윈도는 바이트로, 회선 속도는 비트로 셉니다. 그래서 윈도에 8을 곱해 비트로 바꾼 뒤 왕복 시간으로 나눕니다.
윈도 65,535바이트
왕복 0.1초
65,535 × 8 ÷ 0.1 // 초당 약 524만 비트
초당 약 5메가비트입니다. 회선이 낼 수 있는 양의 200분의 1 정도만 쓰는 셈입니다. 회선은 놀고 있습니다. 받는 쪽의 허락이 모자라 데이터가 안 나갈 뿐입니다.
한 왕복 동안 회선에 실을 수 있는 양을 대역폭 지연 곱이라고 합니다. 위 연결이라면 1기가비트에 0.1초를 곱해 1억 비트입니다. 이것을 8로 나눠 바이트로 바꾸면 약 1,250만 바이트입니다. 윈도가 이 값에 못 미치면 회선을 다 못 씁니다. 윈도 스케일링은 이 틈을 메우려고 생겼습니다.
시프트 수와 배율
윈도 스케일링은 윈도 필드의 폭을 늘리지 않습니다. 대신 필드에 적힌 값을 몇 배로 읽을지 정합니다. 연결을 맺을 때 이 약속을 담아 보내는 칸이 윈도 스케일 옵션입니다. 옵션은 헤더 끝에 붙는 선택 칸입니다.
옵션에 실리는 것은 시프트 수라는 작은 정수 하나입니다. 윈도를 받아 읽는 쪽, 곧 데이터를 보내는 쪽은 윈도 필드의 값을 시프트 수만큼 왼쪽으로 밀어서 읽습니다. 이진수를 왼쪽으로 한 칸 밀 때마다 값이 두 배가 됩니다. 그래서 시프트 수가 n 이면 2를 n 번 곱한 만큼 커집니다. 이렇게 비트를 미는 연산을 비트 시프트라고 합니다.
2를 시프트 수만큼 곱한 값을 이 편에서는 배율이라고 부릅니다. 시프트 수와 배율은 아래처럼 짝을 이룹니다.
| 시프트 수 | 배율 | 필드 값 65,535 를 읽으면 |
|---|---|---|
| 0 | 1 | 65,535바이트 |
| 7 | 128 | 약 838만 바이트 |
| 14 | 16,384 | 약 10억 7천만 바이트 |
시프트 수는 14가 끝입니다. 더 큰 값이 와도 14로 읽습니다. 필드 16비트로 2를 열여섯 번 곱한 값 가까이 적고, 거기에 시프트 14로 2를 열네 번 더 곱합니다. 그래서 윈도 스케일링을 켠 연결의 윈도 상한은 2를 서른 번 곱한 바이트 수, 약 10억 7천만 바이트입니다.
연결을 맺을 때 한 번만 정하는 배율
배율은 연결을 맺는 3방향 핸드셰이크 때 정합니다. 이 절차는 두 컴퓨터가 데이터를 보내기 전에 신호를 세 번 주고받아 연결을 여는 과정입니다. 데이터가 오가기 전에 서로 준비됐는지 확인하려고 거칩니다.
세 신호에는 이름이 있습니다. 첫 신호는 연결을 열자고 청하는 SYN(synchronize, 동기화)입니다. 앞에서 「답」이라고 부른 수신 확인의 정식 이름은 ACK(acknowledgment, 확인 응답)입니다.
둘째 신호는 받은 쪽이 보냅니다. SYN 을 받았다는 ACK 를 자기 SYN 에 얹어 돌려줍니다. 이것을 SYN-ACK 이라고 합니다. 마지막으로 연결을 연 쪽이 ACK 를 보내면 연결이 열립니다.
윈도 스케일 옵션은 이 두 신호에만 실립니다. 연결을 여는 쪽이 SYN 에 옵션을 실어야 합니다. 받은 쪽은 SYN 에 옵션이 있었을 때만 SYN-ACK 에 자기 옵션을 싣습니다. 그 밖의 세그먼트에 붙은 윈도 스케일 옵션은 무시됩니다.
sequenceDiagram
participant 클라이언트
participant 서버
클라이언트->>서버: SYN · 내 시프트 수는 7
서버->>클라이언트: SYN-ACK · 내 시프트 수는 9
Note over 클라이언트,서버: 둘 다 옵션을 보냈으니 윈도 스케일링이 켜진다
클라이언트->>서버: ACK · 윈도 필드 값 1,000
Note over 서버: 1,000 에 128 을 곱해 128,000바이트로 읽는다
그림에서 클라이언트가 보낸 7은 「내가 앞으로 알리는 윈도는 128을 곱해 읽어라」라는 뜻입니다. 서버가 보낸 9도 서버 자신이 알리는 윈도에 걸립니다. 방향마다 배율이 따로 있어서 두 값이 달라도 됩니다.
한쪽이라도 옵션을 안 보내면 양쪽 모두 배율 없이 읽습니다. 서버가 SYN-ACK 에 옵션을 빼면 클라이언트가 7을 보냈어도 두 방향 다 꺼집니다. 한쪽만 켜지는 연결은 없습니다.
SYN 과 SYN-ACK 에 담긴 윈도 필드 값은 배율을 곱하지 않고 읽습니다. 배율은 그 두 신호가 오가고 나서야 정해지기 때문입니다. 한 번 정한 배율은 연결이 끝날 때까지 바뀌지 않습니다.
배율만큼 거칠어지는 윈도
배율을 곱해 읽으면 윈도를 배율 단위로만 적을 수 있습니다. 시프트 수가 7이면 128바이트 단위입니다. 받는 쪽은 남은 공간이 1,000바이트여도 1,000을 적지 못하고 128의 배수로 맞춰 적습니다.
이 때문에 보내는 쪽이 보기에 윈도가 앞서보다 줄어드는 일이 생깁니다. 받는 쪽이 공간을 줄인 것이 아니라 단위에 맞춰 내림한 결과입니다. 그러면 이미 보낸 데이터가 줄어든 윈도의 끝을 넘어선 것처럼 보입니다. TCP 구현은 이것을 오류로 다루지 않게 만들어야 합니다. 윈도가 다시 넓어질 때까지 새 데이터만 멈추고 기다리면 됩니다. 배율이 클수록 단위가 커져서 이런 일이 잦아집니다.
배율을 모르는 중간 장비
두 컴퓨터 사이에는 방화벽이나 로드 밸런서처럼 세그먼트를 들여다보는 장비가 끼곤 합니다. 이런 장비를 미들박스라고 부릅니다. 미들박스가 윈도 스케일링을 모르면 두 가지로 탈이 납니다.
하나는 SYN 에서 옵션을 지워 버리는 경우입니다. 그러면 양쪽 다 배율 없이 연결을 맺습니다. 연결은 멀쩡히 열립니다. 대신 앞에서 본 6만 5천 바이트 상한에 묶여 멀리 있는 서버와의 전송이 이유 없이 느립니다.
다른 하나는 옵션은 통과시키고 장비 자신은 배율을 안 곱하는 경우입니다. 일부 방화벽은 세그먼트가 상대가 알린 윈도 안에 드는지 검사해서 벗어난 것을 이상한 세그먼트로 보고 걸러 냅니다. 이런 장비는 윈도 필드 값을 그대로 읽어 받는 쪽이 아주 조금만 받을 수 있다고 오해합니다. 그 윈도를 넘어선 듯 보이는 세그먼트를 버리면 연결이 멈추거나 끊깁니다.
연결 중간부터 장비가 끼어든 경우에도 같은 일이 생깁니다. 장비가 3방향 핸드셰이크를 못 봤으니 배율을 알 길이 없습니다. 의심이 가면 패킷 캡처 도구로 양쪽 SYN 을 떠서 옵션이 실렸는지부터 봅니다.
효과가 나는 연결과 안 나는 연결
윈도 스케일링이 효과를 내는 것은 대역폭 지연 곱이 6만 5천 바이트를 넘는 연결입니다. 왕복 시간이 길거나 회선이 넓을수록 그렇습니다. 앞에서 본 왕복 0.1초 · 초당 1기가비트 연결이 그렇습니다. 대역폭 지연 곱이 약 1,250만 바이트라 6만 5천 바이트의 190배가 넘습니다. 대륙을 건너는 백업 전송이나 큰 파일 내려받기가 이런 연결입니다.
한 번에 몇 킬로바이트만 오가는 짧은 요청과 응답에서는 차이가 없습니다. 윈도가 작아도 보낼 데이터가 먼저 바닥나기 때문입니다.
윈도는 받겠다는 허락일 뿐 받을 공간 자체가 아닙니다. 큰 윈도를 알리려면 받는 쪽이 그만큼의 수신 버퍼를 잡아 두어야 합니다. 배율이 켜져 있어도 버퍼가 작으면 알리는 윈도도 작습니다.
보내는 쪽에는 윈도 말고 상한이 하나 더 있습니다. 혼잡 윈도는 길목의 망이 지금 얼마나 감당할지 보내는 쪽이 스스로 어림한 양입니다. 받는 쪽 형편이 넉넉해도 망이 막히면 소용없으니 따로 잽니다. 보내는 쪽은 혼잡 윈도와 받는 쪽의 윈도 가운데 작은 쪽만큼만 보냅니다.
관련 항목
이 옵션이 넓혀 주는 윈도
윈도 · 수신 윈도 · 윈도 크기 · 혼잡 윈도 · 수신 버퍼 · 전송 중 데이터량
이 옵션이 기대는 TCP 구성 요소와 연산
TCP · TCP 헤더 · TCP 옵션 · TCP 세그먼트 · SYN · 3방향 핸드셰이크 · 비트 시프트
이 옵션이 필요한지 가르는 지표
대역폭 지연 곱 · 왕복 시간 · 처리량 · 대역폭 · 롱 팻 네트워크
이 옵션과 함께 TCP 를 고성능 망에 맞추는 확장
TCP 타임스탬프 · PAWS · 선택적 확인 응답 · 최대 세그먼트 크기
이 옵션이 탈 나는 원인과 증상
미들박스 · 방화벽 · 로드 밸런서 · 상태 기반 방화벽 · NAT · 윈도 축소 · 제로 윈도
이 옵션이 상한을 풀어 주는 제어 방식
이 옵션의 동작을 들여다보는 도구
패킷 캡처 · tcpdump · Wireshark
다른 이름: window scaling · TCP window scaling · window scale option · 윈도 스케일 옵션 · 윈도우 스케일링 · wscale