• 07. 데이터를 송 · 수신한다

    2021. 10. 3.

    by. ahntree

    728x90

     

    1. 프로토콜 스택에 HTTP 리퀘스트 메시지를 넘긴다

    서버와 연결이 맺어지고 제어권이 돌아오면 데이터 송 · 수신 동작에 들어간다. 송신할 데이터와 함께 Socket 라이브러리의 write을 호출하여 프로토콜 스택에 데이터 송신을 의뢰한다. 이 때, 프로토콜 스택은 데이터의 종류 등에 관한 정보는 모른채 데이터의 길이만 알고 있는 상태이다.

     

     

    데이터 버퍼

    프로토콜 스택으로 데이터가 넘어왔다고 해서 바로 송신 동작이 이루어지지는 않는다. 애플리케이션에서 넘어오는 데이터의 크기가 다양하기 때문에 먼저 데이터를 송신용 버퍼 메모리 영역에 저장했다가 패킷의 최대 용량인 MTU(Maximum Transmission Unit)를 초과하거나 가깝게 차면 송신 동작이 이루어진다. 이더넷에서는 보통 1,500 바이트 정도이다.

     

    MTU에서 헤더 용량을 제한 것을 MSS(Maximum Segment Size)라고 하는데, 이 부분이 데이터가 차지하는 영역이므로 데이터의 용량이 MSS를 초과하거나 가깝게 차면 송신 동작이 이루어진다고 보면 된다.

     

     

    송신 타이밍

    데이터 버퍼에 데이터가 차기만을 기다렸다가는 송신이 계속 지연될 수 있다. 그렇기 때문에 프로토콜 스택은 내부 타이머를 통해 일정 시간이 지나면 송신 동작을 시작한다. 대부분 밀리초 단위로 설정한다.

     

     

    버퍼와 타이밍 사이의 어딘가

    버퍼를 중시하면 송신 속도가 지연될 수 있고, 타이밍을 중시하면 작은 용량의 패킷을 너무 자주 보내게 될 수 있다. 둘 사이의 균형을 맞추는 것이 중요한데 TCP 프로토콜에는 따로 정해진 규격이 없다. 따라서 운영체제마다 설정값이 다를 수 있다.

     

    애플리케이션에서 설정할 수 있는 여지도 있다. 버퍼를 사용하지 않겠다고 설정하면 버퍼를 거치지 않고 바로 데이터를 송신할 수 있다. 데이터 송 · 수신이 빠르게 이루어져야 하는 브라우저 같은 애플리케이션에서 많이 사용할 법한 옵션이다.

     

     

     

    2. 데이터가 클 때는 분할하여 보낸다

    HTTP 요청 메시지의 크기는 보통 MSS보다 작아 한 패킷 안에 넣어 전송하지만, 폼 데이터 등이 추가되었을 경우에는 여러 패킷으로 나누어야 하기도 한다. 송신 버퍼에 있는 데이터를 맨 앞부터 차례대로 MSS의 크기에 맞게 분할하고 한 개씩 패킷에 넣어 전송한다.

     

     

     

    3. ACK 번호를 사용하여 패킷이 도착했는지 확인한다

    TCP 연결을 요청할 때 SYN 패킷의 값을 1로 설정하면서 시퀀스 번호를 무작위 난수로 설정해서 보낸다. 이 초기값을 기억했다가 수신 측에서 몇 번째 데이터까지 받았다는 것을 송신 측에 알릴 때 ACK 번호를 시퀀스 번호 (초기값 + 데이터 길이)로 설정해서 응답한다.

     

    송신 측은 송신한 데이터를 수신했다는 응답을 받을 때까지 송신 버퍼에 저장한다. 응답받은 시퀀스 번호를 해독해 송신한 데이터의 길이보다 수신한 데이터의 길이가 짧을 경우 누락된 패킷들을 다시 송신한다.

     

    오직 TCP 프로토콜을 통해 패킷의 회복이 이루어지기 때문에 다른 어떠한 곳에서도 패킷의 손실을 신경쓰지 않는다. TCP 프로토콜의 또다른 강점이라고 할 수 있겠다.

     

     

     

    4. 패킷 평균 왕복 시간으로 ACK 번호의 대기 시간을 조정한다

    ACK 번호 응답이 돌아오지 않을 수도 있다. 무한정 기다릴 수 없기 때문에 일정 시간을 설정하는데, 이를 타임아웃 값이라고 한다. 적절한 값으로 설정하는 것이 중요한데 인터넷 상황, 서버와의 거리 등 고려할 요인이 너무 많기 때문에 정적인 값으로 설정하는 것이 어렵다.

     

    그래서 TCP는 동적으로 타임아웃 값을 설정한다. ACK 번호 응답이 돌아올 때마다 걸린 시간을 체크해서 적절하게 설정한다.

     

     

     

    5. 윈도우 제어 방식으로 효율적으로 ACK 번호를 관리한다

    데이터 송 · 수신은 병렬적으로 이루어진다. ACK 번호 응답을 기다리지 않고 복수의 패킷을 보낸다는 의미다. 그런데 수신 측의 버퍼 용량을 초과해서 보내면 버퍼를 초과한 만큼의 데이터가 손실될 위험이 있다. 그래서 수신 측에서는 헤더에 윈도우 필드에 수용 가능한 데이터의 크기(윈도우 사이즈)를 명시해서 보낸다.

     

     

     

    6. ACK 번호와 윈도우를 합승한다

    윈도우 통지는 수신 버퍼에 빈 공간이 생길 때 송신 측에 전달하면 된다. ACK 번호는 패킷을 수신했을 때 전달하면 된다. 두 개의 응답은 독립적이기 때문에 불필요하게 많은 응답이 보내질 수 있다.

     

    이를 방지하기 위해 수신측은 윈도우 통지나 ACK 번호를 바로 보내지 않고 잠시 기다린다. 기다리는 사이에 추가적으로 응답해야 한다면 1개의 패킷에 묶어서 함께 보낸다. 복수의 윈도우 통지나 ACK 번호는 가장 나중의 값만 알면 되기 때문에 하나로 줄여서 보낼 수 있다는 이점이 있다.

     

     

     

    7. HTTP 응답 메시지를 수신한다

    1. 브라우저는 리퀘스트 메시지를 송신해달라고 요청하고 응답을 받기 위해 Socket 라이브러리의 read를 호출한다.
    2. 요청을 받은 프로토콜 스택은 수신 버퍼에 수신 데이터를 다 받을 때까지 다른 작업을 하고 있다가 데이터 수신 작업이 완료되면 애플리케이션에 건네준다.
    3. 이후 적절한 타이밍에 윈도우를 송신측에 통지한다.

     

    728x90

    댓글