모든 이야기

수신자가 숨을 고를 시간을 주는 TCP 흐름 제어

TCP 흐름 제어는 송신자가 수신자의 처리 능력보다 많은 데이터를 밀어 넣지 않도록 조절하는 기능이다. 네트워크 경로의 혼잡을 다루는 혼잡 제어와 목적이 다르다. 수신 컴퓨터의 메모리가 충분해 보여도 해당 연결의 수신 버퍼나 응용의 읽기 속도가 병목일 수 있다.

수신 응용이 버퍼를 천천히 비우면 광고 윈도가 줄고 송신자가 보내는 양을 제한하는 그림

흐름 제어는 수신 버퍼를 보호하며 네트워크 혼잡 제어와 함께 전송량을 제한한다.

수신 윈도가 말하는 여유

수신자는 확인 응답 등에 광고 윈도를 실어 자신이 받아들일 수 있는 바이트 범위를 송신자에게 알린다. 송신자는 그 범위를 넘어서 새 데이터를 무제한 보내지 않는다. 윈도는 단순한 패킷 개수가 아니라 바이트 위치와 연결된 전송 허용 범위로 이해해야 한다.

수신 버퍼의 사용 가능한 용량이 64KiB이고 응용이 계속 읽고 있다면 새 데이터가 들어와도 여유가 다시 생긴다. 반대로 응용이 파일 쓰기나 다른 작업 때문에 읽기를 멈추면 버퍼가 찬다. 수신 윈도가 줄어들고 송신자는 확인되지 않은 데이터를 포함한 전송량을 그 범위에 맞춰 제한한다.

윈도가 0이 되면 새 데이터를 계속 밀어 넣을 수 없다. 그렇다고 연결이 바로 실패한 것은 아니다. 응용이 다시 읽으면 여유가 생기고, 윈도 갱신을 통해 전송을 재개할 수 있다. 갱신 정보가 유실된 상황에서 양쪽이 영원히 기다리지 않도록 송신자는 적절한 탐색 절차를 사용한다.

네트워크가 빨라도 속도가 안 나오는 이유

송신자가 실제로 보낼 수 있는 양은 수신 윈도뿐 아니라 혼잡 윈도 등 다른 조건에도 제한된다. 단순하게는 두 윈도 가운데 작은 쪽이 중요한 상한이 되며, 이미 전송 중인 바이트를 고려해야 한다. 수신 윈도가 크다고 경로가 그만큼의 트래픽을 감당한다는 뜻은 아니다.

왕복 시간이 긴 경로에서는 충분한 데이터를 동시에 전송 중으로 유지해야 링크를 활용할 수 있다. 전송 윈도가 64KiB이고 왕복 시간이 100ms인 단순 모델에서는 윈도/왕복 시간 값이 약 655kB/s다. 실제 처리량에는 헤더, 손실, 혼잡 제어와 응용 동작도 영향을 준다. TCP 윈도 스케일 옵션은 큰 윈도를 표현할 수 있게 한다.

버퍼를 크게 설정하는 것은 일부 상황에 도움이 되지만 느린 응용의 근본 처리를 대신하지 않는다. 과도한 버퍼는 메모리를 쓰고 대기를 늘릴 수도 있다. 낮은 전송률을 진단할 때 광고 윈도, 응용의 읽기 패턴, CPU·디스크 사용, 왕복 시간과 혼잡 상태를 함께 보면 수신자 병목과 경로 병목을 구분할 수 있다.

수신 윈도를 관찰할 때 순간적인 값 하나만으로 결론을 내리지 않는 것이 좋다. 응용이 일정 간격으로 큰 덩어리를 읽으면 윈도는 줄었다가 다시 커지는 패턴을 보일 수 있다. 반면 장시간 0으로 유지되면 응용의 읽기 정체, 버퍼 부족, 연결 처리 방식 등을 점검할 근거가 된다. 작은 여유가 생길 때마다 아주 작은 단위로 전송하고 광고하면 헤더와 처리 비용이 커지는 문제도 있으므로 구현은 적절한 갱신 정책을 사용한다. 결국 흐름 제어의 목적은 버퍼를 항상 가득 채우는 것이 아니라, 수신자가 아직 보관하고 처리할 수 있는 범위를 지키면서 안정적으로 진행하는 데 있다. 데이터가 도착하는 속도와 응용이 읽는 속도를 각각 측정하면 어느 쪽을 개선해야 하는지도 분명해진다.

참고 자료