http://www.kocw.net/home/cview.do?mty=p&kemId=1169634
이전 내용을 살짝 복기해보고 시작하자.
Application Layer |
Transport Layer |
Network Layer |
Link Layer |
Physical Layer |
- Transport Layer :
- TCP와 UDP가 대표적이다
- 프로토콜로서 Application Layer에게 기본적인 서비스 기능들은 해준다.
- 멀티플렉싱
- 에러 체킹
TCP는 UDP보다 조금 더 다양한 기능을 갖고 있는데 오늘 중요하게 볼 TCP의 특성은 reliable이다.
Reliable하다는 것은 Application Layer에서 내려온 메시지가 하나도 유실되지 않고 에러없이 receiver에게 잘 전달된다는 것을 의미한다.
하지만 하위의 Network Layer, Link Layer, Physical Layer는 reliable하지 않다.
즉 unreliable할 때 어떻게 reliable할 수 있는지 매커니즘을 이해해 볼 필요가 있다.
그렇기 때문에 TCP의 RDT(reliable data transfer)가 어떻게 이루어지는지 지금부터 살펴보자.
RDT (Reliable Data Transfer)
Unreliable하다는 것은 패킷 유실 혹은 패킷 에러가 발생할 수 있다는 것이다.
즉 패킷 유실과 패킷 에러만 잘 처리하면 "reliable하다"라고 말 할 수 있는 것이다.
RDT protocol 같은 경우는 한 번에 하나의 패킷 하나만 주고받으며, 정상적으로 받았는지 확인이 되면 다음 패킷을 전송한다.
(이 때문에 사실 지금 통상적으로 쓰이는 방식은 아니다)
이제부터 여러 케이스들을 살펴보자.
1. Pkt Error(O) Pkt Loss(X)
패킷 에러는 있는데 유실은 없는 경우이다.
즉 sender가 보낸 메시지는 누락없이 갔는데 내용 자체 문제가 있다는 것이다.
메세지에 에러가 있다는 건 receiver가 어떻게 알까?
바로 error detection을 통해 알 수 있는데, checksum bit를 통해 패킷에 담긴 메세지의 에러 여부를 판단해준다.
- 패킷 수신
- 에러 O
- 피드백이 필요하다. (sender에게 알려야 한다)
- Acknowledgements(ACK) : 제대로 잘 받았다.
- Negative acknowledgements(NAK) : 받긴 했는데 패킷에 에러가 있다.
- sender가 받은 피드백이 1번일 경우 다음 패킷을 전송한다.
- sender가 받은 피드백이 2번일 경우 재전송(Retransmission)해준다.
- 피드백이 필요하다. (sender에게 알려야 한다)
- 에러 X
- 정상 수신
- 에러 O
강의에 나온 것 처럼 실제 전화할 때 사례를 보면 이해가 빠를 수 있다.
A : 있잖아 내가 오늘 강의를 듣는데.
B : 어어 (ACK)
A : 네트워크 중에 TCP 쪽 설명을 듣는데,
B : 어어 (ACK)
A : TCP 특성중에 Reliable이란게 있어서, 두 가지 피드백이 필요하대
B : 응? 뭐라고?? (NAK)
A : TCP 특성중에 Reliable이란게 있어서, 두 가지 피드백이 필요하대
A가 Sender, B가 Receiver라고 봐보자.
A가 계속 패킷(메시지)를 보내면 B는 잘 듣고 있다는 반응(ACK)를 보인다. 그러다 에러가 보이면 다시 물어보고(NAK) A는 재전송(Retransmission)하게 된다.
하지만 이 방법 만으로 신뢰성(reliable)이 있는 대화가 가능할까?
그러기에는 아직 불충분하다.
Receiver가 보내는 피드백이 에러인지 아닌지 판단하지 못한다.(ACK? NAK?)
즉, 피드백에도 checksum이 있어야 한다.(오고 갈때 모두)
에러가 발생했을 때 ACK였는지 NAK인지 모르는 상황이 온다. 이 상황을 모르고 sender는 다시 패킷을 보내게 된다.
여기서 중복 패킷이 발생하게 된다.
또 문제가 될 수 있는 부분은 receiver입장에서 sender가 AA를 보내고 싶었던건지, A를 두 번 보내 중복으로 판단해야하는 것인지 알 수 없다.
이를 위해 각 패킷에 번호를 붙여주기로 했는데, 이 번호가 Sequence number (seq #)이다.
이제 이 seq #로 패킷의 중복 여부를 판단하기로 했기에, receiver가 중복이라고 판단하면 그 패킷을 삭제처리한다.
Sender가 PKT(0)을 보냈는데 receiver는 PKT(1)을 받을 차례여서 ACK 에러를 보냈다. 그러면 sender는 다시 retransmission을 하게 되는데, 동일한 PKT(0)은 중복이기에 삭제해버린다.
seq # 정보는 패킷의 Header / Data 중 header에 하나의 필드로서 들어간다.
seq #는 0번부터 쭉 붙이면 될까? 그건 조금 무식한 방법이 될 수 있는데, seq #가 무한대까지 늘어나는 경우를 생각해보자.
seq #를 저장하기 위한 시퀀스 필드 크기가 점점 커지게 될 것이다. 헤더는 부가적인 정보를 저장하고 보내기 위함이어 커지면 좋지 않다. 즉, 헤더는 작을 수록 좋기에 최소화가 되어야 한다. (편지봉투에 내용이 더 많으면 안좋은 것과 같음)
각각 최소한의 필드만 사용해야하고 크기는 최소화 해야한다.
그렇기에 seq # 필드 크기를 최소화 시켜주는데, 총 2개면 충분하다.
0,1을 돌려쓰면 쉽게 해결될 수 있다.
Sender가 PKT(0)을 보내고 Receiver가 PKT(0)을 받았다 하면 다음에는 PKT(1)이 올거라고 예상할 수 있다.
근데 sendr가 보낸 PKT(0)에 오류가 있어 다시 보내게 되면 PKT(0)으로 중복인 것을 확인 할 수 있다.
오류가 없어서 제대로 PKT(1)을 보냈다면 이제 다시 PKT(0)을 기다리면 된다. (이후 반복)
(이 로직이 가능한 이유는 한 번에 하나의 패킷만 주고 받기 때문이다.)
정리해보면 아래와 같은 흐름으로 이루어진다.
- Sender는 패킷을 보낼 때 seq #를 붙여주고
- Receiver는 패킷을 받아 seq #를 계속 트래킹 한다.
- Sender는 피드백에 대해 에러 체킹 하여 재전송하고, 에러가 없었는데 NAK일 경우에 또한 재전송한다.
- Receiver는 계속 트래킹하면서 중복 패킷은 삭제해주고, 에러가 있다면 NAK을 내보낸다.
지금까지는 ACK와 NAK를 모두 사용했지만 ACK만 사용할 수도 있다.
대신 receiver는 ACK에 seq#를 붙여줘야한다.
혹시나 PKT(1) 받았는데 에러일 경우, ACK(0)을 보내 에러라고 알려준다.
ACK(1)이어야 제대로 받았다는 것을 의미한다 ( ACK(0) = NAK )
2. Pkt Error(O) Pkt Loss(O)
근데 unreliable한 상태에서는 에러 뿐만 아니라 앞서 봤었던 것 처럼 메시지 유실도 발생 가능하다.
즉, PKT Error와 PKT Loss를 모두 잡는 순간 그 떄 reliable하다고 말할 수 있다!
Sender가 메시지를 보냈는데 유실되었다면? Sender는 아무 응답도 들을 수 없다. 왜냐하면 receiver도 받은게 없으니 둘 사이에 정적만이 흐를 뿐이다..! 이 때 정적을 깰 수 있는 건 결국 다시 sender가 보내는 액션이 된다.
즉 처음 메시지를 보내고 sender가 타이머를 통해 시간을 체크하고 시간이 지났는데도 응답이 없으면 다시 보내게 되는 것이다.
- Sender가 PKT 전송 & 타이머 스타트
- 타이머 시간이 끝나도록 응답이 없다면
- 타이머가 종료되어 패킷 유실이라고 판단하고 재전송한다.
과연 이 타이머 시간은 어느정도로 정하는게 좋을까..? 이건 정답이 없는 문제이다.
타이머가 짧은 경우, 긴 경우 모두 장단점이 있기에 적당히 기다리는 것이 최고이라는 것이 정답이라고 말하고 있다.
타이머의 시간 | 장점 | 단점 |
짧을 경우 | - 에러 체킹이 빠르다 - 바로 재전송 할 수 있다 (리커버리가 빠르다) |
- 기다리지 않고 막 전송하기 때문에 네트워크에 오버헤드를 줄 수 있다. |
길 경우 | - 오버헤드는 줄일 수 있다. | - 유실 시 대처가 늦다. |
자 이제 좀 정리가 되었을 거라 생각하고 여러 케이스들을 살펴보자.
(a)의 경우는 에러/유실이 없는 최고의 경우이다.
(b)의 경우는 sender가 보낸 패킷이 유실되었을 때 상황을 보여주고 있다. 타이머가 보냄과 동시에 시작되어, 종료된 이후 재전송하고 문제 없이 진행되고 있는 모습을 볼 수 있다.
sender측에서 발생한 것, receiver 측에서 발생한 것 모두 같은 유실이다.
(c)의 경우는 (b)와 유사하니 보면 된다!
(d)의 경우를 보면 타임아웃이 성급하게 된 것을 알 수 있다. receiver는 제대로 받아서 보내고 있는데 그 사이를 참지 못하고 재전송하게 된 것이다. 이럴 경우, 재전송 받은 패킷의 경우 중복으로 처리가 되기 때문에 삭제처리가 되고(중복되어 삭제하는 경우에도 ACK는 보내줘야함), sender는 타이머가 끝나고 온 회신을 보고 그 다음 패킷을 보내주면 된다!
이처럼 RDT 3.0은 완벽하게 Sender ~ Receiver 사이에 신뢰성 있는 통신을 가능하게 해준다.
내용을 전체적으로 정리해보자.
- Unreliable할 경우
- PKT error
- 극복 방안
- Error Detection
- Feedback
- Retransmission
- Sequence number
- 극복 방안
- PKT loss
- 극복 방안
- Timeout
- 극복 방안
- PKT error
- Packet (TCP header)
- Header
- error detection, seq등과 같은 정보들이 담긴다.
- Header
지금까지 unreliable한 상황에서 reliable 할 수 있도록 하는 매커니즘 이해해본 것이다.
하지만 RDT는 PKT를 하나만 보낼 수 있기에 너무나 단순한 구조이다.
하나를 보내고 돌아올 때 까지 네트워크를 사용하지 않는 것이기에, 넓디 넓은 고속도로에 차를 한대만 보내는 그런 비효율적인 상황이 생기게 된다.
이 때문에 실제로는 한꺼번에 패킷들을 다 쏟아부어 보내고 여러 차례 수신을 받게 된다.
이 처럼 여러 패킷을 동시에 주고 받게 되면 피드백, seq#등의 관리가 중요하고 필요해진다.
그래서 TCP가 복잡한 것 이다...! 이제부터 천천히 알아가보자!