Transport vs network layer
network layer는 host간 logical communication을 담당하며
transport layer는 logical communication between process를 담당한다.
Multiplexing and demultiplexing
process to process delivery를 위해 transport 계층이 제공해야 하는 가장 기본적인 서비스에 해당한다.
Multiplexing이란 socket을 통과하여 트랜스포트 계층으로 내려온 데이터에 목적지 주소가 담긴 헤더를 달아주는 과정을 말한다. 어플리케이션 계층에서 각 프로그램이 서로 다른 소켓을 통해 내려보낸 데이터를 일렬로 줄세우는 형태가 되기 때문에 Multiplexing이라는 용어를 사용한다.
반대로 Demultiplexing은 메세지를 받는 측에서 이루어진다. Multiplexing과 반대로 생각하면 되는데, 일렬로 들어온 데이터를 각 socket에 맞게 나누어주기 때문에 Demultiplexing이라고 한다.
Connectionless Demultiplexing의 동작 과정
Connectionless Demultiplexing이란 UDP의 case를 지칭하는 것이다.
transport 계층에서 만든 segment의 형식은 위와 같다.
위 형식과 같은 segment를 아래 그림과 같은 방법으로 주고 받게된다.
UDP로 데이터 전송 시 destination IP address와 destination port number가 필요하다.
Connection-oriented Demultiplexing의 동작 과정
Connection-oriented에서는 TCP socket을
source IP address, source port number, dest IP address, dest port number를 모두 사용하여 식별한다.
왜냐하면 UDP에서는 하나의 소켓을 이용하여 통신을 수행하지만 TCP에서는 door socket을 이용하여 모든 process 마다 개별적인 socket을 할당하기 때문이다.
따라서 알맞은 socket을 찾기 위해 source의 IP, port 정보가 모두 필요하다.
TCP의 경우 위 그림과 같이 데이터 전송과 demultiplexing이 이루어진다.
socket programming에서 살펴본 바와 같이, TCP는 각 host마다 고유한 socket을 생성하여 데이터를 주고 받는다.
OS는 socket을 생성하면서 fork()를 통해 자식 프로세스를 생성하기 때문에, 각 socket마다 프로세스가 존재하는 것을 확인할 수 있다.
UDP
UDP는 handshaking을 진행하지 않는, connectionless한 프로토콜이다.
UDP는 각 segment를 independent하게 전달하기 때문에 packet의 순서 등을 보장하지 않는다.
UDP는 streaming multimedia, DNS 등에 사용된다.
streaming multimedia의 경우 중간에 패킷이 조금 손실되더라도, 사람의 감각기관으로 인지하기 쉽지 않기 때문에, 속도를 위해 packet loss를 일부 감수할만한 가치가 있기 때문이다.
이 외에도 DNS에도 UDP를 사용하는데, DNS는 한번 요청을 보내서 IP 주소를 받으면 더 이상 데이터를 주고 받지 않는다. 이 한 번의 communication을 위해 connection을 유지하는 것은 overhead가 크기 때문에, UDP 방식으로 데이터를 주고받는다.
UDP의 segment format을 자세히 살펴보면 아래와 같다.
UDP segment는 TCP에 비해 헤더사이즈가 작다는 특징이 있다.
checksum 필드는 UDP segment 전송 과정에서 bit flapping이 발생했는지 체크하기 위한 필드로, 데이터가 온전하게 도착했는지 확인하기 위한 필드이다.
Reliable Transfer를 위해 필요한 Protocol 요소들
TCP에서는 UDP와 달리 reliable transfer를 위한 다양한 프로토콜 요소들이 존재한다.
따라서 TCP를 살펴보기 전에 이러한 요소들을 먼저 살펴보는 것이 이해에 도움이 될 것이므로 먼저 살펴보도록 하겠다.
Checksum
receiver가 전달 받은 segment에 오류가 있는지를 체크하기 위한 요소이다.
Acknowledgement
receiver가 sender에게 전송한 packet을 정상적으로 수신했다고 알리는 과정을 뜻한다.
Negative acknowledgement
receiver가 sender에게 전송한 packet에 오류가 있음을 알리는 과정을 뜻한다.
Timer
만약 전송한 packet 자체가 네트워크에서 drop된 경우, receiver는 아무런 segment도 받지 못한 상태이기 때문에 ACK나 NAK를 전송하지 않는다. 따라서 Sender는 계속해서 응답을 대기하는 상태에 머물러야 하는데, 이를 방지하기 위하여 특정 시간 이후 re-transmit를 하기 위한 timer를 사용한다.
Sequence Number
Timer로 인해 전송되었던 packet이 또 전송될 수 있기때문에 이를 구분하기 위해 Sequence Number를 사용한다.
Window, pipelining
만약 모든 packet 전송 과정에서 ACK 또는 NAK가 돌아올 때 까지 sender가 대기하며 아무런 작업도 하지 않는다면 매우 비효율적일 것이다.
따라서 ACK, NAK를 받지 않고 전송할 수 있는 packet의 수인 window size를 정하고, 이를 이용하여 pipelining을 수행한다.
TCP
TCP는 in-order byte stream으로 데이터를 전송한다.
TCP는 데이터 전송 시 우선 자신의 TCP 버퍼에 데이터를 저장하고, flow control과 congestion control에 따라 속도를 맞춰 데이터를 전송하는데, 이 때 byte stream의 형태로 데이터를 전송하게된다.
TCP의 segment 구조는 위와 같다.
UDP와 동일하게 header와 payload 파트로 나누어져 있는 것을 확인할 수 있다.
TCP의 헤더는 총 20 byte의 고정헤더가 존재하며, 경우에 따라 추가적인 정보가 붙을 수도 있다는 점이 특징이다.
TCP sequence number와 ACK
TCP는 연결 시 어떤 숫자를 sequence number의 시작으로 사용할 지 결정하게된다.
segment의 payload에 담겨있는 첫 번째 byte가 sequence number에 해당한다.
ACK는 다음으로 들어올 segment의 seqeunce number에 대한 내용을 담고 있는데, 이는 TCP가 데이터를 순차적으로 처리기 위한 메커니즘에 해당한다.
위 그림을 통해 TCP Sequence Number, ACK, Window가 데이터 전송 과정에서 어떻게 활용되고 있는지 확인할 수 있다.
좀 더 직관적인 예시를 통해 살펴보면 아래와 같다.
Host A에서 C를 타이핑했을 때, 데이터 전송을 위해 Seq 42, ACK 79의 패킷이 생성되어 Host B로 전송되었다.
이는 Host A가 42번째 데이터를 전송했으며, sequence number 79의 데이터가 자신에게 전달되기를 기대한다는 것을 의미한다.
다음으로 Host B는 이 데이터를 받은 후 Seq 79, ACK 43의 데이터를 전송하였으며, 이는 자신이 79번째 데이터를 전송했고, 43의 seq를 가진 packet을 기대한다는 것을 의미한다.
TCP에서는 이런식으로 데이터의 순서를 보장하는 communication을 수행한다.
TCP round trip time, timeout
TCP가 RTT만큼 대기했는데도 ACK가 전달되지 않는 경우 문제가 발생한 것임을 예상할 수 있기 때문에 이 경우 timeout을 인식하고 re-transmit를 수행할 수 있다.
다만 RTT를 정확하게 알 수 없기 때문에, 예상 시간을 설정하고 해당 시간이 넘어가면 timeout을 발생하는 방식으로 처리하게 된다.
이 때, RTT를 너무 짧게 예측하면 불필요한 재전송이 너무나 많이 발생될 것이며, RTT를 너무 길게 예측하면 segment loss에 대한 reaction이 늦어지게된다.
이를 방지하기 위해 이전에 성공한 데이터 전송과 응답을 통해 RTT의 정보를 얻는다.
EstimatedRTT를 얻기 위한 식은 아래와 같다.
이전까지 예측했던 RTT와 이번에 얻은 RTT를 이용하여 위와 같이 예측 RTT를 계속해서 갱신한다.
다만 EstimatedRTT를 통해 현재 RTT를 예측하는데는 오차가 크기 때문에, 이를 보완하기 위해 DevRTT를 도입한다.
DevRTT가 크게 나온다는 것은 지금까지 예측한 RTT와 이번에 얻은 Sample RTT의 차이가 크다는 것이다.
따라서 현재 네트워크의 RTT 변화가 굉장히 크다는 것을 의미하기 때문에, TimeoutInterval을 크게 잡아서 이러한 변동에 대처해야한다..
즉 이 경우 safety margin의 값을 크게 잡아서, Timeout을 발생시킬 범위를 넓게 잡는다.
TCP의 reliable data transfer
앞서 TCP가 reliable data transfer를 보장하기 위해 필요한 여러가지 요소들에 대해 살펴보았다.
이러한 요소들에는 checksum, ack, seq number, timer, pipelining 등이 있었다.
TCP Sender Event
TCP에서 reliable data transfer를 진행하기 위해, Sender에서 진행하는 event들에 대해 살펴보겠다.
앞서 다양한 프로토콜 계층에 대해 이야기할때, application 계층에서 만들어진 메세지에 각 프로토콜 계층에 맞는 헤더가 하나씩 추가된다는 점을 언급했다.
이 헤더는 다른 host에게 전달되었을 때, 각 헤더에 위치하는 계층의 프로토콜에 의해 읽히고 사용된다.
TCP 연결에서 communication은 양방향 소통으로 이루어지기 때문에 Client와 Server 모두 TCP sender와 TCP receiver가 될 수 있다.
TCP Sender에서 발생하는 이벤트는 아래와 같다.
1. seq number가 적힌 segment를 생성하고, checksum을 계산하여 이를 segment에 적는다.
2. timer가 작동중이지 않았다면, timer를 실행시킨다.
timer는 내보낸 segment중 ACK를 받지 못한 가장 오래된 segment에 대해 설정되어 있으며, timer가 작동중이지 않다는 것은 이전에 보낸 segment에 대해서는 모두 응답을 받은 상태임을 의미한다.
3. timeout 발생 시 segment를 재전송하며, timer를 재 실행시킨다.
위 그림은 TCP sender의 동작을 finite state machine으로 표현한 것이다.
최초에는 NextSeqNum과 SendBase의 값을 최초 SeqNum으로 초기화하고 시작한다.
SendBase는 현재까지 응답을 받은 SeqNum의 가장 최신 값의 직후를 의미하며, NextSeqNum은 다음번에 내보낼 segment의 SeqNum를 의미한다.
SendBase의 정의가 조금 헷갈릴 수 있는데, 간단하게 생각하면 SendBase-1까지의 값이 ACK를 받은 위치라고 생각하면 된다.
data received from application above는 어플리케이션 계층에서 transport 계층으로 데이터가 내려온 이벤트에 해당한다.
해당 이벤트에서는 segment를 생성하고, NestSeqNum을 할당한 후 segment를 전송한다.
이후 `NextSeqNum에 data의 길이를 더해 NextSeqNum을 업데이트`하고, 타이머가 실행중이지 않은 경우 타이머를 실행시킨다.
ACK received, with ACK field value y는 전송한 segment에 대한 ACK를 받은 이벤트에 해당한다.
`y가 sendBase의 값보다 큰 경우 SendBase를 y`로 업데이트한다.
이후 아직 window 내부에 ACK를 받지 못한 segment가 존재하면 timer를 재실행하며, 아닌 경우 timer를 stop한다.
timeout 발생 시 아직 ACK를 받지 못한 segment중 가장 작은 seq number를 가진 segment를 재전송하고, timer를 재실행한다.
간단한 시나리오는 아래와 같다.
오른쪽 시나리오를 보면 Host A에서 `Seq 92` 데이터를 전송한 이후 ACK가 도달하기 전에 timeout이 발생하여 재전송이 발생한 것을 확인할 수 있다.
Host B에서는 재전송된 데이터에 대해 ACK를 보내주는데, ACK값이 120으로 할당된 것을 확인할 수 있다.
Seq가 92이고 8바이트인데 왜 ACK가 100이 아닌 120을 전송했을까?
이는 TCP가 `cumulative ACK`방식을 사용하므로 이전에 전송했던 ACK값을 바탕으로 ACK값을 전송하기 때문이다.
이러한 방식의 장점은 아래 시나리오에서 확인할 수 있다.
Host A가 두 개의 segment를 전송했는데, 첫 번째 segment의 ACK가 loss된 상황이다.
그렇다면 Host A는 해당 segment를 재전송해야만할까?
cumulative ACK를 사용한 경우 이는 문제가 되지 않는데, 두 번째로 전송한 segment에 대한 ACK 120이 정상적으로 돌아왔으므로 이전에 보낸 segment가 정상적으로 도착했음을 보장받을 수 있기 때문이다.
TCP Receiver Event
in-order segment의 도착 시, ACK를 500ms정도 지연시킨 후 전송하게 된다. 이는 다음 segment의 입력에 의해 한 번에 ACK를 전송할 수 있는 기회가 있을 수 있기 때문이다. 이는 pipelining에 의해 segment가 연속적으로 전송되는 상황이기에 효율적으로 동작한다.
in-order segment 도착 이후 delay하는 상황에서 다시 in-order segment가 들어오는 경우, 즉시 ACK를 전송한다.
예상하는 시퀀스 번호보다 더 큰 seq number를 가진 out-of-order segment가 도착하는 경우, 받아야 하는 sequence number가 포함된 duplicate ACK를 전송한다.
TCP fast retransmit
timeout interval은 상당히 넉넉하게 계산되는 경향이 있다.
따라서 특정 loss가 발생하고 복구되기까지 상당히 많은 시간이 소요되는데, 이를 보완하기 위해 duplicate ACK를 lost segment의 detect를 위해 사용한다.
TCP fast retansmit에서는 3번의 duplicate ACK를 받는 경우 packet loss가 발생했다고 판단하고, retransmit를 수행한다.
위 그림은 fast retransmit가 발생하는 시나리오를 표현한 그림이다.
'Network' 카테고리의 다른 글
[Computer Network] TCP의 flow control과 congestion control (0) | 2023.11.07 |
---|---|
[Computer Network] Socket Programming (0) | 2023.11.01 |
[Computer Network] DNS (Domain Name System) (0) | 2023.10.31 |
[Computer Network] Application 계층의 Protocol (0) | 2023.10.31 |
[Computer Network] Internet Layer (0) | 2023.10.29 |