https://growth-coder.tistory.com/154
이전 포스팅에서 TCP가 reliable한 이유에 대해서 알아보았다.
이번 포스팅에는 정확히 어떤 방식으로 TCP가 reliable한지 RDT의 버전을 기준으로 공부해보려고 한다.
FSM(Finite State Machine)을 사용할 예정이므로 간단하게 알고가면 좋다.
아래 그림에서 보다싶이 위에는 발생한 이벤트, 아래는 이벤트가 발생하면 진행할 액션이다.
그리고 점선 화살표가 나올텐데 이는 가장 처음 상태라는 의미이다.
RDT 1.0
sender
sender부터 보면 sender의 첫 상태는 application 계층으로부터 call을 기다리는 상태이다.
기다리는 상태에서 application 계층에서 rdt_send를 호출하면 transport 계층에서는 데이터를 바탕으로 패킷을 만들고 패킷을 보낸다.
그리고 상태는 다시 call을 기다리는 상태로 돌아간다.
receiver
receiver는 아래쪽에서부터 call을 기다리는 상태이다.
만약 rdt_rcv를 호출하면 패킷으로부터 데이터를 꺼내고 applicatioin 계층으로 데이터를 전달한다.
그런데 이 방식은 bit error가 발생한다면 해결할 방법이 없다.
이를 보완한 방식이 rdt 2.0이다.
RDT 2.0
bit error가 발생했을 때를 대비하여 rdt 2.0에서는 ACK와 NAK가 등장한다.
우선 checksum을 사용하여 bit error를 감지할 수 있다.
bit error가 발생하면 NAK를, 발생하지 않으면 ACK를 sender에게 반환하는 것이다.
sender
1.0과 달리 sender의 상태가 하나 추가되었다.
기존의 위쪽으로부터 call을 기다리는 상태와 ACK 또는 NAK를 기다리는 상태 두 가지가 존재한다.
application 계층이 rdt_send를 호출하면 패킷을 만들고 udt_send로 패킷을 보낸다.
이 때 상태 call을 기다리는 상태가 아니라 ACK 혹은 NAK를 기다리는 상태로 변경하는 것이다.
에러를 의미하는 NAK을 받는다면 udt_send로 패킷을 다시 보내고 다시 ACK 혹은 NAK를 기다리는 상태로 돌아온다.
만약 ACK를 받는다면 통신이 잘 이루어졌다는 뜻이므로 아무 액션을 취하지 않고 application 계층으로부터 call을 기다리는 상태로 이동한다.
receiver
receiver의 상태는 1.0과 마찬가지로 call을 기다리는 상태 하나이다.
에러를 감지했다면 NAK를 보내고 정상이라면 패킷으로부터 데이터를 추출해내고 application 계층으로 데이터를 전달한다.
그리고 sender에게는 잘 받았다는 ACK를 보낸다.
RDT 2.0에서도 문제가 존재한다.
바로 ACK 혹은 NAK를 보낼 때 에러가 발생할 수도 있다는 문제이다.
RDT 2.1
RDT 2.1에서는 ACK, NAK 에러 문제를 해결하기 위해 패킷마다 Sequence Number를 부여한다.
이것으로 패킷이 이전 것인지 현재 것인지 구분할 수 있고 duplicate 문제를 해결할 수 있다.
sequence number가 0이면 이전 것이고 1이면 현재 것이다.
이렇게 구현하려면 stop and wait 방식을 사용해야 한다.
sender는 패킷을 보내고나서 응답을 기다리는 것이다.
또한 ACK나 NAK의 에러 여부도 검사해야 하기 때문에 receiver는 ACK와 NAK를 sender에게 보낼 때에도 checksum을 함께 보내야 한다.
sender도 ACK나 NAK를 받을 때마다 checksum을 사용하여 에러 여부를 감지하여 에러가 발생했다면 다시 패킷을 보내고 응답을 기다린다.
sender
sender의 상태는 이전 것, 현재 것을 구분해야하므로 4가지이다.
application 계층이 rdt_send를 호출하면 sequence number (0), 데이터, checksum을 바탕으로 패킷을 만들고 udt_send로 패킷을 보낸다.
그리고 sequence number가 0인 ACK 혹은 NAK를 기다리는 상태로 이동한다.
NAK가 오면 다시보내고 ACK가 오면 그 다음 rdt_send를 기다린다.
application 계층이 rdt_send를 호출하면 sequence number (1), 데이터, checksum을 바탕으로 패킷을 만들고 udt_send로 패킷을 보낸다.
그리고 sequence number가 1인 ACK 혹은 NAK를 기다리는 상태로 이동한다.
NAK가 오면 다시보내고 ACK가 오면 그 다음 rdt_send를 기다린다.
receiver
이전 것은 sequence number가 0, 현재 것은 sequence number가 1인 것을 생각하고 보면 쉽게 이해할 수 있다.
ACK나 NAK를 보낼 때에도 checksum을 같이 보낸다는 점을 유의해야한다.
RDT 2.2
RDT 2.2는 NAK-free protocol이다.
NAK을 쓰지 않고 ACK만 쓰는 것이다.
2.1에서 sequence number를 통해 이전 것인지 현재 것인지 구분할 수 있게 되었다.
처음 rdt_send 요청을 받으면 sequence number는 0이다.
그리고 sequence number 0이 포함되어있는 패킷을 보내고 ACK 0을 기다리는 상태로 이동한다.
receiver 쪽에서는 에러가 발생하지 않았다면 ACK 0을 보내고 에러가 발생했다면 ACK 1을 보내는 것이다.
sender는 ACK 0을 받아야 하는데 ACK 1을 받았다면 에러가 발생했다고 판단하고 다시 sequence number가 0인 패킷을 보내고 ACK 0을 기다린다.
에러 문제는 어느정도 해결이 되었지만 패킷 손실에 대한 문제는 해결되지 않았다.
RDT 3.0
RDT 3.0에서는 패킷 손실 문제를 해결하기 위해 time-out 기능을 사용한다.
일정 시간을 정해두고 응답이 오지 않는다면 패킷을 잃어버린 거라 생각하고 다시 패킷을 보내는 것이다.
sender
Wait for ACK 0 상태를 자세히보면
잘못된 ACK를 받거나 ACK에서 에러가 발생했다면 아무것도 하지 않는 모습을 볼 수 있다.
이전 버전에서는 이럴 경우 다시 패킷을 보냈는데 아무것도 하지 않는 이유는 timeout 기능이 추가되었기 때문이다.
아무것도 하지 않으면 자연스럽게 시간이 지나고 시간이 다 지나면 다시 패킷을 보내는 것이다.
그렇다면 왜 바로 패킷을 재전송 하는 것이 아니라 timeout을 발생시키는 것일까?
바로 premature timeout / delayed ack 문제를 해결하기 위해서이다.
sender는 패킷을 보내고 응답을 받을때까지 시간을 설정한다고 했는데 응답이 오는 시간이 timeout보다 길다면 문제가 발생한다.
위와 같이 ack1을 받는 시간이 길어서 timeout이 발생했다고 하자.
타임 아웃이 발생하여 패킷 1을 재전송했는데 그 때 이전에 받았어야할 ack1이 도착한다.
다시 보낸 패킷 1의 경우 receiver 쪽에서 duplicate 라고 판단하고 다시 ack1을 보낼 것이고 이러한 상황이 반복될 것이다.
그런데 잘못된 ACK를 받았을 때 다시 보내는 게 아니라 아무것도 하지 않는다면 이 문제를 해결할 수 있다.
잘못된 ACK인 ACK 1을 받으면 기다리게 하면 이후에 ACK 0이 날아오면 올바른 ACK로 판단하고 이후의 과정이 정상적으로 진행되게 된다.
receiver
receiver는 2.2와 똑같다.
패킷을 잃어버리는 경우는 receiver가 영향을 받지 않기 때문이다.
'공부 > Network' 카테고리의 다른 글
[Network] Network layer(네트워크 레이어)와 Router (0) | 2023.05.29 |
---|---|
[Network] TCP RDT (0) | 2023.04.27 |
[Network] pipelined protocol (0) | 2023.04.18 |
[Network] 인터넷 프로토콜 스택 (OSI 7계층 ,TCP/IP 5계층) 및 패킷 전송 과정 (0) | 2023.04.12 |
[Network] Transport Layer (다중화/역다중화, TCP가 reliable한 이유) (0) | 2023.04.11 |
댓글