최근에 네트워크에 관심이 생겨 네트워크에 대해 앞으로 조금씩 정리해볼 예정이다.
흐름제어란 송신 측이 수신 측의 처리 속도보다 더 빨리 데이터를 보내지 못하도록 제어해 주는 것을 의미힌다.
수신 측이 송신 측보다 속도가 빠른 것은 아무 문제가 되지 않지만 송신 측이 수신 측보다 속도가 빠르면 문제가 발생한다.
무슨 문제가 발생할까?
수신 측에서 수신된 데이터를 저장용량(queue)에 저장하고 처리해서 위에 계층으로 서비스를 하게 되는데,
만약에 송신 측에서 보내는 데이터의 속도가 더 빠르다면 제한된 저장용량(queue)를 초과하여 이후에 도착하는 데이터의 손실을 가져올 수 있다.
이것을 오버플로우(Overflow)
이라고 표현한다.
그렇다면 송신과 수신 측간에 불필요하게 응답과 재전송이 이루어지기 때문에 효율이 떨어진다.
이러한 오버플로우
가 일어나지 않기 위해 수신 측에서 데이터를 그만 발송하라는 메시지를 송신측에 통보해야 하는 피드백 메커니즘이 필요하다.
이를 흐름 제어
이라고 한다.
데이터를 수신하는 노드가 전송하는 노드에게 현재 자신의 상태에 대한 정보를 보내주는 것이다.
위 문제를 해결하기 위해서는 두 가지 방법이 있다.
stop and wait
과 Sliding-Window
방식이다.
이 방법의 개념은 간단하다.
매번 전송한 패킷에 대해 확인 응답을 받아야만 그다음 패킷을 전송하는 방법이다.
즉, 수신 측에서 오류 없이 데이터가 무사히 도착했다는 메시지인 ACK
를 보낸다.
송신 측에서 이러한 메시지인 ACK
를 받으면 다음 데이터 블록을 전송한다.
이에 따라 송신 측은 ACK
의 유무에 따라 다음 데이터 블록을 전송하거나 재전송한다.
- Timeout을 설정한 후 Frame(data)을 전송하고
- 이 시간(Timeout) 동안 수신 노드로부터
ACK
이 도착하지 않으면 전송에 실패한 것으로 간주하고 - 같은 Frame을 다시 전송하는 것이다.
다만, 이 방법은 다음과 같은 문제가 발생한다.
- Frame이 손실되는 경우
- ACK가 손실되는 경우
- Timeout 기간이 너무 짧은 경우 --> 수신 노드가 ACK을 보내도 이미 Timeout이 돼버려 송신 노드는 같은 Frame을 재전송
위와 같은 문제로 효율적이지 않지만 가장 큰 문제는 일정 시간 동안 한 개의 Frame밖에 보내지 못한다는 것이다.
그래서 이러한 효율성을 개선하고자 나온 방법이 Sliding-Window
방식이다.
이 방법은 수신 측에서 윈도우 사이즈를 설정하여 이 크기만큼 세그먼트를 연속적으로 전송하는 기법이다.
매 세그먼트(Segment) 전송 시마다 ACK
를 수신한 후 전송하게 되면 RTT가 길 경우 단위 시간당 데이터 전송량이 매우 떨어지게 된다.
RTT(Round-Trip Time)은 패킷이 목적지에 도달한 후, 그에 대한 응답이 돌아오기까지의 시간이다. 쉽게 말하자면 패킷의 왕복 시간이다.
따라서 효율적으로 전송하기 위해 수신 측에서 받을 수 있는 범위 내에서 연속적으로 전송한다.
수신 측에서 설정한 윈도우 크기만큼 송신 측에서 ACK
를 받지 않아도 세그먼트를 전송할 수 있게 하여 데이터 흐름을 동적으로 조절하여 제어하는 기법이다.
이 방식에서는 각 프레임에 순서 번호(Sequence Number)를 부여한다.
이것은 수신 측에서 기대하는 다음 프레임의 순서 번호를 포함하는 ACK
를 송신 측으로 보내줌으로써 계속 받을 수 있는 프레임들의 번호를 알려준다.
예를 한번 들어보겠다.
먼저, 송신자(A)와 수신자(B)는 각자 버퍼를 준비한다.
A는 자신의 버퍼에 데이터를 쓰고, B는 자신의 버퍼에서 데이터를 읽어오는 것이다.
- A가 B에게 100,000 byte의 파일을 전송하겠다!
- 그러면 TCP가 이 파일을 100개의 패킷으로 나눈다(가정)
- A와 B 사이에 연결이 된다.
- B가 자신의 Receive Window Size를 A에게 알려준다.
- A는 이 Size에 맞추어 B에게 데이터를 전송한다.
사진을 보면서 이해해보자.
송신자 A의 버퍼 상황이다.
A는 각 패킷에 번호를 매기고 패킷마다 Time을 건 다음 B에게 패킷을 전송한다.
위와 같은 상황에서 B가 3번 패킷을 받았다는 ack을 보내면 A는 8번 패킷을 전송한다.
이것처럼 윈도우에 포함되는 모든 패킷을 전송하고 패킷들의 전달이 확인되는 대로 윈도우를 옆으로 옮김(Slide)으로서 다음 패킷을 전송한다.
- 만약 B가 자신의
RWS
가 3,000 bytes라고 하면 A는30
개의 패킷을 전송한다.(window = 30) - 송신 측에서
Seq #
를 부여하여window
사이즈인30
만큼 수신 측으로 전송한다. - 수신 측은
Seq #1
을 받으면Ack #31
을 보낸다. - 송신 측은
Ack #31
을 받으면 한 칸씩window
를 옮겨Seq #31
을 수신 측에게 전송한다.
즉
ack #
와 다음 패킷을 보낼seq #
가 같다.
- 만약 Time 안에 각 패킷에 대한
ack #
이 오지 않으면ack #
을 받지 못한 패킷에 대해 재전송을 한다. 따라서 A는 자신이 전송한 가장 마지막 순서 패킷의 번호와 가장 최근에 받은 ack의 번호를 기억해야한다.
위 그림의 경우 2번은 A가 가장 최근에 받은 ACK 패킷의 번호이고, 7번은 A가 전송한 패킷 중 가장 마지막 순서 패킷의 번호이다.
이것을 왜 기억해야 하는가?
만약에 seq #2
를 보냈으나 ack #32
가 오지 않은 상황이다.
그렇다면 seq #1
과 ack #31
까지는 잘 전송되었다는 의미이기도 하다.
송신 측에서 Seq #2
를 다시 전송하기 위해 seq #1
과 ack #31
을 기억해야 '여기까지 잘 왔구나' 생각하여 다음 패킷을 보낼 수 있다.
이 동영상을 참고하면 이해에 도움이 될 것이다. (4분부터 시청 권장)
B의 데이터를 읽는 속도가 떨어질 경우, WS(Window Size)를 줄여가며 송신자의 데이터 전송을 컨트롤할 수 있다.
또한 수신 쪽의 확인(응답)을 받지 않고도 윈도우 크기만큼 데이터를 보내는 것이 가능하므로 stop-and-wait
보다 네트워크를 효율적으로 사용할 수 있다.
이번 시간에는 호스트
와 호스트
간의 데이터 처리를 효율적으로 하는 Flow Control
에 대해 알아봤다면
다음 시간에는 호스트
와 네트워크
상의 데이터 처리를 효율적으로 하는 Congestion control
에 대해 알아볼 것이다.