3.1 transport layer services
transport services and protocols
트랜스포트 레이어가 하는 일
- 서로 다른 호스트에서 돌아가는 어플리케이션 프로세스 간 logical communication을 제공한다.
- 프로토콜은 엔드 시스템에서 돌아간다
-> 코어에 있는 라우터들은 트랜스포트 레이어를 구현하고 있지 않음.(라우터들을 거쳐 데이터들이 전달되지만 트랜스포트 레이어의 입장에서는 거쳐가는 과정은 보이지 않고 두 엔드 시스템 사이의 logicla한 커뮤니케이션만 보게 됨)(라우터에서는 실제로 네트워크, 데이터 링크, 피지컬 레이어만 존재)
-> sender가 하는 일 : segment 단위로 메세지를 잘라서 트랜스포트 레이어의 헤더를 붙이고 네트워크 레이어로 보내줌
-> receiver가 하는 일 : 세그먼트들을 합쳐서 메세지를 만들고 어플리케이션 레이어로 전달 - TCP / UDP
Transport vs network layer
트랜스포트 계층은 프로토콜 스택에서 네트워크 계층 바로 상위에 존재한다.
차이점은 트랜스포트 계층 프로토콜은 서로 다른 호스트에서 동작하는 프로세스 사이의 통신을 제공하지만,
네트워크 계층 프로토콜은 호스트들 사이의 논리적 통신을 비교한다.
예를 들자면, 트랜스포트는 아파트 내부의 동, 호수끼리 통신한다면 네트워크는 아파트끼리 통신하는 것이다.
Internet transport layer protocols
- TCP : reliable(신뢰성 있게 전달, 보낸 그대로 도착하도록), in-order delivery(보낸 순서대로 받는사람이 받음)
- 전송 속도 조절(congestion control, flow control)
- 커넥션을 맺어야 통신이 가능하다.(connection setup) - UDP : unreliable(신뢰성x, 중간에 날아갈 수 있음), unordered delivery(보낸 순서 상관 없이 도달)
- 전달 역할만 수행한다. - 두 서비스 모두 안해주는 것들
- 딜레이 보장 : 언제까지 도달하도록 보장 해주는것.
- 속도 유지 보장(bandwidth guarantee) : 예를들어 스트리밍 중에 계속 몇bps로 속도를 유지하도록 해 주는것.
3.2 multiplexing and demultiplexing
일단 알아야할 것은 애플리케이션 계층과 트랜스포트 계층 사이에 존재하는 소켓의 역할은
네트워크에서 프로세스로 데이터를 전달하고, 프로세스에서 네트워크로 데이터를 전달하는 출입구 역할을 한다는 것이다. 사실 이 부분이 다중화, 역다중화의 역할과 매우 비슷하다.
1) 다중화
앞서 배운 캡슐화(Encapsulation)에서 트랜스포트 계층의 역할은 소켓에 세그먼트를 붙이는 작업을 한다고 배웠다.
이는 사실 다중화를 위함인데, 다중화는 각 데이터에 헤더 정보로 캡슐화하고, 그 세그먼트들을 네트워크 계층으로 전달하는 작업을 한다.
위 그림에서 다중화는 서버 프로세스 p1, p2에서 각각 클라이언트에 분배되는 p3, p4로 올바르게 갈 수 있게끔
데이터에 정보를 붙여 아래 계층으로 내보내는 것을 의미한다.
2) 역다중화
역다중화는 다중화와는 반대로 그림의 서버 프로토콜 스택을 기준으로 아래에서 위로 올리는 부분이다.
이때 트랜스포트 계층 세그먼트의 데이터를 올바른 소켓으로 전달하는 작업을 한다.
따라서 p3, p4에서 온 프로세스들을 각각 p1, p2로 옮겨주는 기능을 한다.
즉, 네트워크 레이어는 ip 주소를 보고 가기 때문에 두 클라이언트 모두 한 서버로 가고, TCP가 두 프로세스 중 어느 프로세스로 갈지 결정해 주는 역할을 한다. 이때,
- sender에서의 multiplexing : 여러 소켓에서의 데이터를 다루기 위해 트랜스포트 헤더를 붙여서 보내준다.
- receiver에서의 demultiplexing : 헤더를 보고 어느 소켓으로 보낼 지 결정한다.
connectionless demultiplexing -> UDP
- 서버에서의 동작
- 소켓 생성
- 서버의 addr 정보를 넣어주고 바인드 해줌
- 소켓으로 부터 메세지 읽어 들임(recvfrom) -> 클라이언트의 정보를 저장
- 클라이언트쪽으로 메세지 보냄(sendto) -> 앞서 저장한 클라이언트의 정보로 보냄
- 클라이언트에서의 동작
- UDP 소켓 생성
- 서버의 정보 저장
- Sendto
- Recvfrom
-> 서버와 클라이언트 둘 다 tcp와 달리 커넥션 맺고 끊는게 없다.
- UDP 패킷을 보낼 때 목적지 주소와 포트번호를 정해줘야 한다.
-> 서버가 UDP 패킷을 받으면 헤드에 포함된 목적지의 포트 번호를 확인하고 이 포트 번호를 사용하는 소켓으로 보내줌. - 같은 목적지 주소와 같은 목적지 포트 번호를 가지고 있는 패킷은 같은 소켓으로 보내진다.
-> 보내는 쪽의 주소와 포트 넘버가 달라도 이렇게 동작함.
connection-oriented demultiplexing -> TCP
우선, TCP 소켓과 UDP 소켓의 차이점은 TCP 소켓은 4개 요소들의 튜플 (출발지 IP 주소, 출발지 포트 번호, 목적지 IP 주소, 목적지 포트 번호)에 의해서 식별된다는 것이다.
따라서 TCP 세그먼트가 호스트에 도착하면, 호스트는 해당되는 소켓으로 세그먼트를 전달하기 위해서 4개의 값을 모두 사용한다. (역다중화)
- TCP 에서의 demux : receiver는 네가지 정보를 모두 활용하여 segment를 적절한 소켓으로 전달해 준다.
- 웹 서버는 TCP를 사용하는데, 클라이언트와 커넥션을 맺을 때 마다 다른 소켓을 사용한다.
-> 사용하는 포트번호와 ip 주소가 같더라도 source의 정보들이 다르기 때문
-> non-persistent HTTP는 요청마다 새 커넥션을 하기 때문에 모두 다른 소켓을 사용한다.
-> TCP의 연결보장성 확인.
3.3 connectionless transport : UDP
UDP : User Datagram Protocol
UDP segment의 특징
- best-effort(UDP segment 가 갑자기 사라질수도 있고 전달 순서가 뒤바뀔 수 있음)
- no frills, bare bones 라고 불리는 인터넷 트랜스포트 프로토콜
- connectionless(커넥션이 없다, handshaking이 없다)
- UDP는 언제 사용하는가?
-> streaming multimedia apps : 손실이 일어나도 되고, 속도가 중요한 경우
왜 UDP를 사용하는가?
- 커넥션 맺을 필요 없음(딜레이 없음)
- 간단하다
- 헤더 사이즈가 작다(tcp는 20바이트)
- 보낼 수 있을 만큼 빠르게 보냄(tcp는 속도 조절함)
UDP : segment header
어플리케이션 메세지를 segment들로 나누며, 헤더와 payload(데이터) 부분으로 나눈다.
UDP의 헤더 필드에는 length, checksum이 있다.
- length : UDP segment의 길이이며, 그림상 16비트지만 단위가 바이트이기 때문에 2^16= 약 65535 바이트라고 봐야 한다! 따라서 64KB이고 이 크기가 UDP 세그먼트가 보낼 수 있는 크기이다.
- checksum : 에러를 판별하기 위한 부분으로 바로 밑에서 자세히 설명하겠다.
UDP checksum
checksum의 목적 : 전송된 segment에 대해 오류 판단
sender
- 세그먼트의 내용들을(헤더 + payload) 16비트로 나눠서 관리.
- 16비트의 수들을 다 더해 checksum에 저장.(1의 보수 사용)
receiver
- 받은 세그먼트에 대한 checksum을 계산.
- 받은 세그먼트를 16비트씩 나눠서 다 더해서 checksum과 비교
- cheksum과 다르면 에러로 판단, 같으면 에러 없다고 판단
But, checksum이 같다고 에러가 없다는 것을 의미하는것은 아님(특정 부분에서 빠지고, 다른 부분에서 더해지면 총 합은 같기 때문)
예시
- 16비트가 두개만 있다고 가정
- 두개를 더한다
-> 자리수가 올라가면 wrap around(앞의 1을 맨 뒤에 더해줌) 해준다
-> 체크섬에는 1의 보수 취해서 저장(0,1 바꿔서)
UDP checksum calculation(실제 동작 과정)
checksum을 일단 0으로 해 둔다.
-> ip pseudo 헤더 부분까지 포함하여 checksum을 계산해서 저장한다. (ip는 네트워크 레이어의 도움을 받아야함)
-> 중간에서 끝나면 16비트 만큼 나머지 0으로 채워준다.(16비트로 나누는데 앞에서 끝나버리면 나머지 부분들은 0으로 채워줌)
3.4 principles of reliable data transfer
어떻게 data를 reliable 하게 보내는가
먼저 한 가지 가정은, 보내어지는 패킷들이 순서대로 전달된다는 것이다.
패킷의 순서를 바꾸지 않고 데이터의 흐름대로 설명할 것이다.
위에 나온 용어 정리
- rdt_send() : 프로토콜의 송신 측은 이 호출에 의해 위의 계층에 의해서 호출이 된다.(call : 위에서 아래로)
- rdt_rcv() : 패킷이 채널의 수신 측으로부터 도착했을 때 호출된다.(callback: 아래에서 위로)
- deliver_data() : 수신자 측이 데이터를 반대로 넘길 때 호출된다.
- udt_send() : 비신뢰적인 데이터 전송(unreliable data transfer)으로서, 송신, 수신 측이 패킷을 양방향으로 전달할 때 사용될 것이다.
동작하는 함수들
이 그림은 FSM(유한 상태 머신)이며 화살표는 단방향(unidirectional)으로 동작하게 된다.
이 FSM에는 3가지 요소가 있는데 각각 state, event, actions이다.
- state : 각각 동그라미를 노드라고 보면, 노드 안에 쓰여 있는 글자들이다. 다음 이벤트를 결정하는 중요한 요소다.
- event : 전송이 일으키는 사건을 의미한다.
- actions : 이벤트가 발생될 시 취하는 행동을 뜻한다.
rdt 1.0: reliable transfer over a reliable channel
가장 이상적인 프로토콜로, 단순하며 따로 예외 처리해주지 않아도 된다.
하지만 이상일뿐 실제로는 존재하지 않는 프로토콜이다.
- bit error가 없다. (수신 측이 피드백을 제공할 필요가 없다.)
- 패킷의 loss가 없다.
rdt 1.0: FSM specification
- sender : 어플리케이션 레이어가 데이터를 보내주면 패킷을 만들고(데이터에 헤더를 붙임) 패킷을 아래 레이어로 전송한다.
- receiver : 아래 레이어로부터 패킷을 받으면 패킷에서 헤더 떼서 데이터 추출하고 데이터 전달
rdt 2.0: channel with bit errors
rdt 2.0 은 비트 오류에 대한 해결책으로 재전송(retransmission)을 제시한다.
재전송을 하려면 수신자가 잘 받았는지 못 받았는지 말을 해줘야 알 수 있는데 rdt2.0에서는 수신 측의 피드백을 통해 이를 처리한다.
수신 측의 피드백
수신자는 ACKs 또는 NAKs를 송신 측에 전달하는데
acknowledgements(ACKs)는 잘 받았다는 의미이고, negative acknowledgements(NAKs)는 비트 에러가 발생했다는 의미이다.
-> sender 입장에서 NAK이 오면 에러가 있다는 의미이므로 재전송하고 ACK이 오면 잘 받았다는 의미이므로 그냥 넘어감.
rdt 2.0: FSM specification
- sender
->데이터를 받으면 패킷을 만들고 보내준 후 ACK이나 NAK을 기다리는 상태로 변경(receiver의 응답 대기)
-> receiver로부터 패킷을 받고 만약 받은 패킷이 NAK이면 재전송
-> 패킷 받고 만약 ACK이라면 아무런 action 취하지 않고 상태만 변경 - receiver
-> 패킷을 받고 받은 패킷이 corrupt라면(checksum을 확인해서 에러 있는 경우) NAK을 보냄
-> 받은 패킷이 notcorrupt라면 데이터를 추출하여 전송해 주고(어플리케이션 레이어로), sender에게 ACK을 보냄
rdt 2.0의 치명적인 에러들
- 애초에 ACK/NAK 피드백들이 손상된 경우 sender는 알 수 없다.
- 따라서 sender가 손상된 피드백들을 잘못 이해하여 재전송한 경우, 명령이(action) 복제될 가능성이 있다.
해결책 : 데이터 패킷에 새로운 필드를 추가하고 이 필드 안에 순서 번호를 삽입한다. (복제 여부나 순서 확인 가능)
결과 : 순서 번호를 보고, 재전송된 것인지 새로운 패킷을 전송한 것인지 알 수 있다.
rdt 2.1: handles garbled ACK/NAKs
rdt 2.0의 수정된 버전이다.
- rdt 2.1의 FSM은 전보다 두 배 많은 상태를 가지고 있다.
이는 전송되거나 기다리고 있는 패킷이 순서 번호 0 또는 1을 가져야 하는지 반영해야 하기 때문이다. - 순서 번호가 0 또는 1밖에 없는 이유는 두 가지 수만 반복해도 최근과 이전 패킷을 구분할 수 있기 때문이다.
rdt 2.1: FSM specification
- sender
-> 첫 상태 : 데이터 받으면 헤더에 0 붙여서 패킷 만들고 보내고 상태 변경
-> 뭔가 받았고, corrupt이거나 NAK이면 재전송하고 상태 유지(상태 : ACK이나 NAK 0를 기다림) / 뭔가 받았고, notcorrupt이고, ACK이면 상태 변경
-> 데이터 받으면 헤더에 1 붙여서 패킷 만들고 보내고 상태 변경
-> 위 내용 반복 - receiver
- -> 0번 기다리는 상태 : 뭔가 받았는데 문제없고(notcorrupt), 0번이면 데이터 손질해서 애플리케이션 레이어로 보내주고, ACK 만들어서 샌더로 보내주고 상태 변경
-> 문제있으면 NAK 만들어서 보냄 / 문제 없는데 1번이면 ACK은 만들어서 보내줌
-> 둘 다 애플리케이션쪽으로 보내는거 없음, 상태도 변경 아님.(문제가 있거나 이미 받은 패킷이니까)
-> 1번 기다리는 상태 : 위 내용 반복
논의점
- sender
-> 패킷에 sequence number를 붙인다.
-> 0,1 만 써도 충분하다. -> stop and wait을 하기 때문
-> 받은 ACK/NAK 이 corrupte인지 확인 해야 한다.
-> ACK이 와야 다음 패킷을 보낸다.
-> 상태가 두배 많다(전에 온 넘버 기억, 어떤 넘버가 올지 대기) - sender
-> 받은 패킷이 중복된것인지 확인해야한다.
rdt 2.2: a NAK-free protocol
- 일반적으로 NAK을 안쓰고 ACK만 쓰는 경우가 많음 -> NAK을 안쓰도록 한 버전.
- 기능은 2.1과 같지만 NAK을 사용하지 않는다.
- NAK 대신에 ACK 활용 : receiver는 제대로 받은 마지막 패킷에 대한 ACK을 보내준다.
-> 이것을 위해 ACK에 sequence number 넣어서 보내줌 - sender에서 받은 중복된 ACK이 NAK의 기능을 한다.
-> 중복된 ACK 받으면 현재 패킷 재전송함
rdt 3.0: channels with errors and loss
이전까지의 rdt 버전들은 비트 에러에 대해서만 처리했지, 패킷이 아예 유실되는 경우에 대해서는 처리하지 않았다.
만약 ACK이 유실되어 오지 않았다면, 송신자는 뭘 보내야 할지 도무지 알 수가 없다.
그래서 패킷이 손실되지 않고 제대로 갔는지 타이머를 통해 확인해야 하는데, 이 기법을 타임 아웃이라고 한다.
rdt 3.0: sender FSM specification
타이머의 동작은 다음과 같다.
- 1. 패킷을 송신하는 시간에 타이머를 시작(송신측에서)
- 2. 만약 ACK가 오기 전에 타이머가 끝나면 재전송
- 3. ACK가 왔다면 타이머를 멈춤
하지만 패킷이 유별나게 큰 지연 시간을 가진다면 데이터 패킷이나 그 패킷에 대한 ACK가 손실되지 않았다 하더라도 패킷을 재전송할 수 있다. 이것은 송신자 - 수신자 채널에서 중복 데이터 패킷(duplicate)의 가능성을 포함한다.
하지만 우리는 이미 rdt 2.2에서 sequence number를 통해 중복되는 경우를 처리할 수 있다.
rdt 3.0 은 기능적으로 정확한 프로토콜이지만, 패킷을 하나하나 보내서 RTT(전송시간)의 문제가 있다.
Pipelined protocols
파이프 라인 프로토콜은 마치 송신자와 수신자 사이에 파이프를 연결하여, 송신자가 여러 개의 패킷을 마치 비행기를 타고 날아가는 것처럼 보내게 된다.
이 경우 여러 개의 패킷을 동시에 보내기 때문에 sequence number를 0과 1 뿐 아니라 여러개로 늘려야 하며
여러개의 패킷을 담을 수 있게 버퍼를 만들어 주어야 한다. 하지만 전송시간이 매우 빠르다.
보통 파이프라인 프로토콜은 두 가지 형태가 있는데, go-back-N과 selective repeat 기법이다.
정해진 개수의 여러개의 패킷을 보내고, ACK이 오는대로(하나씩 올 때 마다) 다음 패킷을 보낸다.
-> 효율성 정해진 개수배 만큼 늘어난다.
-> performance 측면에서 좋아진다.
-> 단점 : 프로토콜이 복잡해진다.
Go-Back-N
GBN 프로토콜에서 송신자는 확인 응답을 기다리지 않고, 여러 패킷을 전송할 수 있다.
하지만 조건이 있다. 파이프라인에서 확인 응답이 안 된 패킷의 최대 허용 수 N보다 크지 않아야 한다는 점이다.
특징
- 송신자는 확인응답이 되지 않은 N개의 패킷을 파이프라인 안에서 가질 수 있다.
- 수신자는 받은 패킷들에 대해서 마지막 패킷에 대해서만 ACK를 보낸다.(누적 ack)
- 송신자는 가장 오래된 '전송되었지만 아직 확인 응답 오지 않은 패킷'에 대한 타이머만 사용한다.
GBN 프로토콜은 위 그림과 같이 직사각형의 윈도를 가진다. 이를 슬라이딩 윈도라고 한다.
가장 오래된 패킷의 순서 번호를 send_base로 정의하고, 사용되지 않은 가장 작은 순서번호를 nextseqnum으로 정의한다면, 순서 번호의 범위에서 4가지 패킷을 식별할 수 있다.
- 1. 이미 확인 응답받은 패킷
- 2. 전송하였지만, 아직 확인 응답받지 않은 패킷
- 3. 사용 가능하지만 아직 전송하지 않은 패킷
- 4. 사용할 수 없는 패킷
GBN의 중요한 특징
- 만약 base 패킷이 아닌 이후의 패킷에 대한 ack 응답이 먼저 왔다면, 그 이전의 패킷들은 모두 ack 처리한다.
- 만약 패킷 n+1이 먼저 도착했고, n이 수신되었는데 n이 손실되었다면(타임 아웃도 포함) n이상의 패킷들은 모두 재전송해야 한다.
Selective repeat : 선택적 반복
앞서 설명한 GBN의 특성 중 이전 패킷에 대한 손실이 발생하면, 쓸데없이 다른 패킷들도 재전송한다는 불필요한 액션을 한다는 것을 배웠다.
선택적 반복은 이러한 GBN의 단점을 보완한 프로토콜로, 오류가 발생한 패킷만을 송신자가 다시 전송함으로써
불필요한 재전송을 피한다.
- Individual ack 사용(따로 관리해서 받은 패킷만 ack 보내고 못받은 패킷은 ack 안보내줘서 못받은 패킷만 재전송 할 수 있게).
- 각각의 패킷에 대한 타이머 사용.(ACK이 오면 타이머 끄고 expire 되면 그 패킷만 재전송 하면 됨)
- 타임아웃 된 패킷에 대해서만(ack 못받은 패킷) 재전송
문제점
위 그림처럼 만약 시퀀스 넘버가 3가지로 이루어져 있고, 윈도 크기가 3이라면
전송 중에 같은 시퀀스 넘버가 동시에 움직일 경우, 송수신자에게 혼란을 야기할 수 있다.
따라서 시퀀스 넘버는 적어도 윈도 크기의 2배는 넘어야 이 문제를 해결할 수 있다.
참고자료
https://suhwanc.tistory.com/105?category=781986
'CS 공부 > 네트워크' 카테고리의 다른 글
Chapter 3 - Transport Layer(2) (0) | 2021.09.27 |
---|---|
Chapter2 - Application Layer(2) (0) | 2021.09.21 |
Chapter2 - Application Layer(1) (0) | 2021.09.21 |
Chapter 1 - Computer Networks and the Internet (0) | 2021.09.05 |