네트워크 원리 chapter 2 - 3

4 minute read

3. 데이터를 송/수신한다.

1. 프로토콜 스택에 HTTP 요청 메시지를 넘긴다

  • connect 동작이 완료되면 데이터 송/수신 동작에 들어간다. 이 동작은 애플리케이션이 write를 호출하여 송신 데이터를 프로토콜 스택에 건네주는 곳부터 시작된다.

  • 프로토콜 스택은 받은 데이터의 내용에 무엇이 쓰여있는지 알지 못한다. 해당 길이만큼만 바이너리 데이터가 1바이트씩 차례로 나열되어 있다고 인식한다.

  • 데이터를 곧바로 송신하는 게 아니라 일단 자체의 내부에 있는 송신용 버퍼 메모리 영역에 저장하고 애플리케이션이 다음 데이터를 건네주기를 기다린다. 데이터를 저장하는 이유는, 한 번의 송신 의뢰에서 건네주는 데이터의 길이는 애플리케이션의 사정에 따라 결정되고, 프로토콜 스택에서 제어할 수 없다. 이런 상황에서 받은 데이터를 곧바로 보낸다면 작은 패킷을 많이 보내는 경우가 생긴다. 그렇기에 어느 정도 데이터를 저장하고 나서 송/수신 동작을 한다.

어느 정도까지 저장하고 송신할 지 판단하는 두 가지 요소

  1. 한 패킷에 저장할 수 있는 데이터의 크기 (MTU)
  • MTU는 한 패킷으로 운반할 수 있는 디지털 데이터의 최대 길이로, 이더넷에서는 보통 1500바이트이다. 맨 앞부분에는 헤더가 포함되는데, 헤더를 제외한 것이 하나의 패킷으로 운반할 수 있는 데이터의 최대 길이가 되고, 이것을 MSS라고 한다. 애플리케이션에서 받은 데이터가 MSS를 초과하거나 MSS에 가까운 길이에 이르기까지 데이터를 저장하고 송신한다.
  1. 타이밍
  • 프로토콜 스택은 내부에 타이머가 있어서 일정 시간이 경과하면 MTU 여부에 상관없이 패킷을 전송한다.

  • 전자를 중시하면 네트워크 이용 효율이 높아지지만 송신 동작이 지연될 우려가 있다. 후자를 중시하면 지연은 적어지지만 이용 효율이 떨어진다. 이 둘에 대한 절충 규정은 없으며, 개발자에게 달려있다.

  • 애플리케이션측에서 송신의 타이밍을 제어할 수 있다. 데이터 송신을 의뢰할 때 옵션을 지정할 수 있고, 브라우저 같은 경우는 버퍼에 머무는 만큼 응답 시간이 지연되기 때문에 옵션을 사용해서 바로 보내도록 할 수 있다.

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

  • 폼을 사용하여 긴 데이터를 보낼 경우 등 한 개의 패킷에 들어가지 않을 만큼 긴 것도 있다. (긴 문장을 등록하는 경우)

  • 송신 버퍼에 저장된 데이터는 MSS 길이를 초과하므로 다음 데이터를 기다릴 필요가 없다. 따라서 송신 버퍼에 들어있는 데이터를 맨 앞부터 차례대로 MSS의 크기에 맞게 분할하고, 분할한 조각을 한 개씩 패킷에 넣어 송신한다.

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

  • TCP에는 송신한 패킷이 상대에게 올바르게 도착했는지 확인하고, 도착하지 않았으면 다시 송신하는 기능이 있으므로 패킷을 송신한 후에는 확인 동작으로 넘어간다.

  • 데이터를 조각으로 분할할 때 조각이 통신 개시부터 따져서 몇 번째 바이트에 해당하는지를 세어둔다. 그리고 데이터의 조각을 송신할 때 세어둔 값을 TCP 헤더에 기록하는데, 시퀀스 번호라는 항목이 해당된다. 패킷 전체의 길이에서 헤더 길이를 빼면 데이터의 크기를 계산할 수 있으므로 수신측에서 이 방법에 따라 크기를 산출한다.

  • 누락이 없는 것을 확인하면 수신측은 그 이전에 수신한 데이터와 합쳐서 데이터를 몇 번째 바이트까지 수신한 것인지 계산하고, 그 값을 TCP 헤더의 ACK 번호에 기록하여 송신측에 알려준다. 이렇게 ACK 번호를 되돌려주는 동작을 수신 확인 응답이라고 부른다.

  • 실제로 시퀀스 번호는 1부터 시작하지 않고 난수를 바탕으로 산출한 초기값으로 시작한다. 데이터의 송/수신을 시작하기 전에 초기값을 상대에게 알리게 되어 있다.

  • TCP는 상대가 데이터를 받았는 지 확인할 때까지 송신한 패킷을 송신용 버퍼 메모리 영역에 보관해둔다. 그리고 대응하는 ACK 번호가 상대로부터 돌아오지 않으면 패킷을 다시 보낸다. 이렇기 때문에 다른 곳에서 오류를 회복 조치할 필요가 없다.

  • TCP는 몇 번 다시 보낸 후 회복의 전망이 없는 것으로 보고 데이터 송신 동작을 강제로 종료하고 애플리케이션에 오류를 통지한다.

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

  • ACK 번호가 돌아오는 것을 기다리는 시간을 타임아웃 값이라고 한다. 대기 시간은 너무 짧지도, 길지도 않은 적절한 값으로 설정해야 한다. 하지만 이것은 어려운 것이다. 그래서 TCP는 대기 시간을 동적으로 변경하는 방법을 취하고 있다. ACK 번호가 돌아오는 시간을 기준으로 대기 시간을 판단하는 것이다.

  • 데이터 송신 동작을 실행하고 있을 때 항상 ACK 번호가 돌아오는 시간을 계측해둔다. 그리고 ACK 번호가 돌아오는 시간이 지연되면 이것에 대응하여 대기 시간도 늘린다. 반대로 ACK 번호가 곧바로 돌아오면 대기 시간을 짧게 설정한다.

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

  • ACK 번호가 돌아올 때까지 아무 일도 하지 않고 기다리는 것은 시간 낭비이다.

  • 윈도우 제어는 한 개의 패킷을 보낸 후 ACK 번호를 기다리지 않고 차례대로 연속해서 복수의 패킷을 보내는 방법이다.

  • 그러나 ACK 번호를 기다리지 않고 차례로 패킷을 보내면 수신측의 능력을 초과하여 패킷을 보내는 사태가 일어날 수도 있다.

  • 수신측의 TCP는 패킷을 수신하면 일단 수신용 버퍼 메모리에 데이터를 일시 보관한다. 수신측에서는 ACK 번호를 계산하거나 조각을 연결하여 원래 데이터를 복원한 후 애플리케이션에 건네주어야 한다. 그런데 애플리케이션에 건네주는 속도보다 빠른 속도로 데이터가 도착하면 수신 버퍼에 데이터가 차곡차곡 쌓여서 곧 넘쳐버린다.

  • 이 문제는 수신측에서 송신측에 수신 가능한 데이터 양을 통지하고, 송신측은 이 양을 초과하지 않도록 송신 동작을 실행하는데, 이것이 윈도우 제어 방식의 개념이다.

  • TCP 헤더의 윈도우 필드에서 이것을 알려준다. 수신 가능한 데이터 양의 최대값을 윈도우 사이즈라고 부르고, 보통 수신측의 버퍼 메모리의 크기와 같은 크기가 된다.

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

  • 윈도우 통지가 필요한 것은 수신측이 수신 버퍼에서 데이터를 추출하여 애플리케이션에 건네주었을 때이다. 수신측에서 애플리케이션에 데이터를 건네주고 수신 버퍼의 빈 영역이 늘어났을 때, 이것을 송신 측에 통지해야 하는데 이것이 윈도우 통지의 타이밍이다.

  • ACK 번호는 데이터를 수신한 후 즉시 보낸다고 볼 수 있다.

  • 설명대로라면 ACK 번호 통지와 윈도우 통지의 패킷이 하나씩 따로따로 송신측에 보내지는 데, 이것은 효율성이 저하된다.

  • 수신측은 ACK 번호나 윈도우를 통지할 때 소켓을 바로 보내지 않고 잠시 기다렸다가 다음 통지 동작이 일어나면 한 개의 패킷으로 묶어서 보낸다.

  • 복수의 ACK 번호 통지가 연속해서 일어나는 경우도 최후의 것만 통지하고 도중의 것은 생략해도 상관 없다.

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

  • 브라우저는 요청 메시지를 송신해 달라고 의뢰하고, 서버에서 돌아오는 응답 메시지를 받기 위해 read 프로그램을 호출한다. read를 경우하여 프로토콜 스택에 제어가 넘어가고, 프로토콜 스택이 움직이기 시작한다.

Leave a comment