전송 계층의 프로토콜 TCP와 UDP 특징
TCP의 특징
- 신뢰적이고 순차적인 전달
(에러 없이 전달)
- 혼잡 제어
- 흐름 제어
- 연결 설정
신뢰성있는 데이터 전송의 원리
- 점진적으로 송/수신 측의 reliable transfer protocol(rdt)를 발전 시킬 것입니다.
- 단방향 데이터의 전송만 고려 -> but, 제어 정보는 양방향으로 오갈 것입니다.
- 송/수신 측을 명시하기 위해 Finite State Machine(FSM)을 사용할 것입니다.
상태(state)
-> 어떤 "상태"일 때 다음 이벤트에 의해 다음 상태가 결정. 그래서, 상태들이 서로의 동작을 결정하는 것을 상태 전이(transition)이라고 합니다. 유한한 갯수의 상태가 존재하고, 특정 상태를 정의하는 것을 FSM
이라고 합니다.
rdt 1.0
rdt1.0
은 신뢰적인 채널 상에서 신뢰적인 전송을 말합니다.
즉, 데이터의 bit 오류가 없고 패킷 손실이 없는 경우, 즉 이상적인 상황을 뜻합니다.
송신 측 FSM -> 응용 계층에서 데이터가 내려오면 데이터를 send
수신 측 FSM -> 네트워크 계층에서 데이터가 올라오면 데이터를 receive
-> 각각 송/수신 측이 에러를 신경 쓸 필요없이 데이터를 보내고 받기만 하면 되지만, 이러한 경우는 이상적일이고 비현실적입니다.
rdt 2.0
rdt1.0은 이상적인 상황이고 실제 세상에는 에러가 있을 수 있습니다.
그래서 rdt 2.0
은 비트 오류 검출을 위해 checksum을 사용합니다.
-> 오류는 검출했다고해도 오류를 어떻게 복구할 것인가? -> 재전송으로 해결 합니다.
acknowledgements(ACK)
-> 수신측이 패킷을 잘 받았다고 OK를 송신측에게 보냅니다.
negative acknowledgements(NAKs)
-> 수신측은 패킷에 오류가 발생했다고 송신측에게 명시적으로 알립니다.
-> 송신 측은 NAKs를 받으면 패킷을 재전송합니다.
위 그림과 같이 FSM으로 rdt2.0을 구체화 해보면 에러 검출과정이 추가되어 rdt1.0보다 약간 복잡해졌습니다.
rdt2.0
의 과정은 아래와 같습니다.
1. 송신(sender)측의 FSM -> above(어플리케이션 계층)으로부터 전송할 데이터가 있는지 기다리는 상태
2. 수신(receiver)측의 FSM -> 네트워크 계층으로부터 수신할 데이터를 기다리는 상태
3. 송신(sender)측의 FSM -> 전송한 데이터의 응답인 ACK or NAK을 기다리는 상태
4. 송신(sender)측의 FSM -> 만약, 데이터를 받았고(rdt_rcv) 패킷에 오류가 있다면(isNAK) 데이터를 재전송합니다(udt_send)
rdt 2.0에 있는 치명적인 결함 -> ACK/NAK가 손상되면 어떻게 될 것인가?
- 송신 측은 수신 측에 무슨 일이 일어났는지 알 수 없다.
- 무작정 재전송할 수도 없다: 중복 가능성 때문.
중복의 처리
- 송신 측은 ACK/NAK가 손상되었으면 현재 패킷을 재전송합니다.
- 각 패킷에는 순서 번호(sequence number)를 추가 -> 수신측에는 순서번호에 맞는 ACK를 보낼 수 있게 됩니다.
- 수신 측은 중복 패킷은 버립니다.
전송 후 대기(stop and wait)
-> 패킷마다 순서 번호를 정해서 송신측은 하나의 패킷을 보내고 수신 측의 응답을 기다리는 방법이며 재전송 기법 중에서 가장 단순한 방법입니다.
rdt 3.0
rdt3.0
은 손실될 수 있는 패킷에 대한 처리를 추가합니다.
새로운 가정 -> 하위 채널에서 패킷 (데이터, ACK)가 손실될 수도 있습니다.
- checksum, 순서 번호, ACK, 재전송 등이 도움은 되지만 충분하지 않습니다.
접근방법 -> 송신 측은 "합리적인" 시간 동안 ACK를 기다립니다.
- ACK가 일정시간 내에 안오면 재전송 (timeout)
- 만일 패킷이 없어진게 아니라 지연만 된 것이라면
-> 재전송으로 인해 중복이 발생하지만, 순서 번호로 해결 가능
-> 수신측은 ACK하는 패킷의 순서번호를 명시
- countdown timer가 필요
(a) 그림은 정상적으로 데이터 전송이 완료되었을 경우입니다.
(b) 그림은 송신 측에서 전송한 패킷이 중간에 손실된 상황입니다. 이럴 경우 송신 측으로 ACK가 오지 않으므로 문제가 생겼음을 감지하고 timeout 조건이 만족되면 손실된 패킷을 재전송합니다.
(c) 그림은 송신 측에서 패킷을 정상적으로 전달 했지만 ACK가 오다가 손실된 경우입니다. 이럴 경우에는 수신 측에 패킷이 잘 도착했지만 송신측은 ACK가 오지않아서 정상적으로 도착했는지 모르기 때문에 timeout 조건이 만족되면 패킷을 다시보냅니다. 그러면 수신 측 입장에서는 중복된 패킷이 도착하게 되므로(detect duplicate) 패킷이 손실되었다는 것을 감지하고 중복으로 도착한 패킷은 버리고 다시 ACK를 보내줍니다.
(d) 그림은 패킷 ACK의 응답이 너무 지연이 되는 경우이고 가장 처리가 복잡한 경우입니다. ACK 전송이 매우 늦게되고 ACK가 도착하는 도중에 송신 측은 timeout 조건이 만족하여 패킷을 재전송하게 됩니다. 송신 측에서 패킷을 재전송하고 난 후에 지연되었던 ACK가 도착하게 됩니다. 그러면, 수신측은 중복이 감지되어 중복에 대한 ACK를 보내고 송신측은 뒤늦게 도착한 ACK에 대한 패킷을 보내는 과정을 여러번 거치면서 정상적으로 돌아오게 됩니다.
이와 같은 일이 벌어지는 경우는 timeout의 값이 적당한 값이 아니라 짧게 설정 되어있는 경우 이러한 상황이 일어날 수 있습니다. 반대로 또한, 이러한 경우 처럼 패킷의 전송 속도가 매우 느린것은 네트워크 상태가 매우 혼잡한 상황이라는 것을 알 수 있습니다.
UDP의 특징
- 비신뢰적이고 비순차적 전달
(에러가 없는 것을 보장하지 않음)
- 'best effort'
(최선형, 데이터를 목적지에 전송하는 것을 최우선으로 함). IP에 약간의 기능만 추가
즉, TCP와 UDP가 속한 전송 계층은 네트워크 계층에서 IP 주소를 보고 목적지 호스트에 도착한 후에, 존재하는 수 많은 프로세스 중 Port#를 통해 원하는 프로세스까지 찾아가는 역할을 담당합니다.
다중화(Multiplexing) / 역다중화(Demultiplexing)
다중화(Multiplexing)
-> 여러개의 채널이 하나의 네트워크 라인을 공유해서 사용할 수 있는 것. e.g) 예를 들면 위 그림에서, Process1에서 전송하는 데이터와 Process2에서 전송하는 데이터가 같은 전송계층의 TCP를 공유해서 사용하는 것.
역다중화(Demultiplexing)
-> 공유된 채널에서 나온 데이터가 목적지를 향해 찾아가는 것. e.g) 예를 들면 위 그림에서, 다중화와는 반대로 데이터를 받을 때 네트워크 계층과 전송 계층을 거쳐 Process1으로 갈 수도 있고 Process2로 갈 수도 있는 경우 프로세스를 구분하고 목적지를 결정하는 것. 어느 프로세스로 갈지는 포트 번호로 결정.
다수의 어플리케이션이 존재할 때 TCP를 사용하는 어플리케이션과 UDP를 사용하는 어플리케이션이 각각 존재할텐데 다수의 어플리케이션이 TCP를 공유하거나 UDP를 공유해서 사용 가능합니다.
송신자(sender)의 다중화
-> 여러 소켓에서 나온 데이터들을 처리하고 트랜스포트 헤더를 추가(추후 역다중화에서 사용)
위 그림에서 보면, Process1에서 전송하는 데이터나 Process2에서 전송하는 데이터가 같은 프로토콜을 공유해서 사용할 수 있습니다.
수신자(receiver)의 역다중화
-> 다중화에서 사용했던 헤더 정보를 이용해서 수신한 세그먼트를 알맞은 소켓에 전달
예를 들면 위 그림에서, 데이터를 응답할 때 전송 계층의 트랜스포트 헤더 정보를 이용해서 Process1의 소켓으로 가거나 Process2의 소켓으로 갈 수 있습니다.
역다중화의 작동 원리
TCP/UDP segment
에는 각각 16비트의 출발지(source) 포트넘버와 목적지(dest) 포트넘버가 있습니다.
전송계층에서는 이 포트 번호로 프로세스를 구분합니다. 즉, 네트워크 계층의 IP 주소로 목적지 호스트까지 찾아가고 호스트 안에서 전송계층의 포트 번호로 목적지 프로세스의 소켓을 찾아가는 것입니다. 응답을 받을 때도 마찬가지로 출발지 포트 번호를 보고 출발지 프로세스의 소켓을 찾아갑니다.
소켓(sockets)은 목적지 프로세스가 애플리케이션 계층과 트랜스포트 계층을 연결하여 전송 계층으로부터 데이터를 전달받는 부분 입니다.
특히, UDP와 같은 비연결형(connectionless) 데이터 전송 전에 연결과정 없이 데이터를 전송합니다. 그러므로, 데이터 수신에 대한 응답 과정이 없으며 출발지 IP와 포트 번호가 달라도 목적지 IP와 포트 번호만 있으면 segment의 전송이 가능합니다. 또한, 비연결형이므로 하나의 프로세스(소켓)가 여러개의 상대방 프로세스(소켓)와 통신이 가능합니다.
비연결형의 역다중화
UDP와 같은 비연결형(connectionless) 데이터 전송은 데이터 전송 전에 연결과정 없이 데이터를 전송합니다. 그러므로, 데이터 수신에 대한 응답 과정이 없으며 출발지 IP와 포트 번호가 달라도 목적지 IP와 포트 번호만 있으면 segment의 전송이 가능합니다. 또한, 비연결형이므로 하나의 프로세스(소켓)가 여러개의 상대방 프로세스(소켓)와 통신이 가능합니다.
연결형의 역다중화
연결지향형의 역다중화 -> 연결형이기 때문에 소위 4-tuple이라고 부르는 출발지의 IP와 포트넘버 그리고 목적지의 IP와 포트넘버를 알아야 통신이 가능합니다. 수신측에서는 이 4-tuple의 값을 이용해서 segment를 적절한 소켓으로 전달합니다. 또한, 서버는 동시에 여러 TCP 소켓을 지원할 수 있습니다.
비지속 연결인 HTTP의 경우에는(웹서버의 포트번호 80번) 하나의 소켓을 사용하지 않고 각 클라이언트의 연결에 대해 다른 소켓을 사용하여 역다중화 합니다.
서버에 여러개의 프로세스 있을 경우
위 그림 같은 경우 가운데가 서버이고, 서버에 3개의 다른 프로세스(P4, P5, P6)가 동작하고 있고 클라이언트(P2, P3)에서 서버로 데이터를 전송하는 것을 나타낸 그림입니다. (여기서, 서버의 TCP 포트 번호가 80번이라는 것은 HTTP 프로토콜을 사용하는 웹서버라는 것을 알 수 있습니다.)
클라이언트에서 서버로 전송한 3개의 segment가 하나의 80번 포트로 역다중화 되지만 3개의 다른 어플리케이션 프로세스의 소켓으로 역다중화 되는 것을 설명한 그림입니다.
서버에 하나의 프로세스가 있을 경우
위 그림도 마찬가지로 가운데가 서버이지만, 하나의 프로세스(P4)가 동작하고 클라이언트(P2, P3)에서 서버로 데이터를 전송하는 것을 나타낸 그림입니다.
위 그림과 같이 서버에 프로세스가 하나일 경우에는 각기 다른 스레드(Thread)가 동작하여 여러 소켓에서 데이터를 역다중화 할 수 있습니다.
파이프라인(Pipelined) 프로토콜
송신 측이 다수의 ACK되지 않은 패킷을 "in-flight"로 전송하는 것을 허용합니다.(stop and wait과는 반대)
- 순서 번호의 범위가 증가해야 합니다.
- 송수신 측에 버퍼가 필요합니다.
파이프라인 프로토콜의 일반적인 두 유형: go-Back-N, selective repeat
Go-back-N
1. 송신 측 파이프라인에 최대 N개의 ACK 안 된 패킷이 있을 수 있다
2. 수신 측은 누적확인 응답(cumulative ack) 함. -> 앞에 빠진 부분이 있는 패킷은 ACK를 보내지 않음.
3. 송신 측은 ACK 안 된 패킷 중 가장 오래된 것의 타이머 관리
-> 타이머가 종료되면 ACK가 안 된 모든 패킷 재전송
Selective Repeat
1. 송신 측 파이프라인에 최대 N개의 ACK 안 된 패킷이 있을 수 있다.
2. 수신 측은 각 패킷에 개별적인 ACK를 보냄 ->
3. 송신 측은 ACK 안 된 패킷에 타이머 관리
-> 타이머가 종료되면, ACK 안 된 패킷만 재전송
UDP(User Datagram Protocol)
UDP(User Datagram Protocol)
는 RFC 768 문서에 정의된 비연결 지향 프로토콜로 TCP와 달리 흐름제어, 단편화 및 전송 보장등의 기능을 제공하지 않습니다.
책에서는 'no frills(장식이 없고)', 'bare bones(빼빼 마른)' 이라고 설명하는데 쓸데없는 기능을 전부 빼고 데이터를 목적지에 도달시키는 것만을 목적으로 필요한 기능만 있는 프로토콜이라는 뜻입니다.
UDP의 특징
'best effort'(최선형) 서비스
-> UDP segment는 사라지거나 순서가 바뀔 수 있습니다.
connectionless(비연결형)
-> 송/수신측 사이의 handshaking이 없고 각 UDP의 segment는 목적지 주소만 가지고서 독립적으로 다뤄집니다.
UDP의 사용 예시
streaming multimedia apps
-> 손실을 허용하고 전송률에 민감하므로 스트리밍에 적합합니다. but, 최근에는 HTTP, 즉 TCP를 사용하는 서비스가 많이 생겨나고 있습니다. (보안등의 이슈로 UDP 트래픽을 허용시키지 않는 도메인이 많고 버퍼링을 이용해서 TCP의 문제를 해결하여 사용합니다.)
DNS(Domain Name System)
-> 일반적으로는 UDP를 사용하지만 보안 및 신뢰성의 이유로 TCP를 사용하기도 합니다.
SNMP(Simple Network Management Protocol)
-> 네트워크 관리를 위한 프로토콜.
만약 UDP에서 신뢰적인 전송을 하고 싶다면? -> 전송 계층인 UDP에서는 해결할 수 없고 애플리케이션 계층에서 해주어야 합니다.
UDP 세그먼트 헤더(segment header)
application data(payload)
-> 어플리케이션 계층의 원본 데이터
sorurce, dest port #
-> 출발지와 목적지의 각각 16비트의 포트넘버 입니다.
length
-> UDP segment가 몇바이트인지 나타냅니다.
checksum
-> 16비트의 에러 검출 코드.(처리하지는 않지만 수신자 어플리케이션이 에러를 인지하고 처리할 수 있도록)
UDP가 존재하는 이유?
연결 설립이 없습니다. -> 지연을 유발하지 않습니다.
단순합니다. -> 송/수신자 간의 연결 상태를 유지하지 않습니다.
헤더 사이즈가 작습니다. -> 빠르게 데이터를 보낼 수 있습니다.
혼잡 제어가 없습니다 -> 빠르게 데이터를 보낼 수 있습니다.
TCP(Transmission Control Protocol)
TCP(Transmission Control Protocol)
는 연결 지향형(connection-oriented)이며 신뢰성 있는 바이트 스트림을 제공하는 프로토콜입니다.
여기서 연결지향형이란 응용 프로그램이 데이터를 교환하기 전에 TCP 연결을 통해 데이터를 송수신할 수 있는 연결 통로를 만들고 데이터를 전송해야 함을 의미하고, 신뢰성이 있다는 말은 데이터를 성공적으로 수신했거나 오류가 발생했음을 알려주는 것을 의미합니다.
즉, 상대방과 통신할 때 연결상태를 확인 후 데이터 전송을 시작하고 통신 종료때까지 연결 상태를 유지하며 오류가 발생하면 수신자에게 알려주는 프로토콜 입니다.
TCP의 특징
점대점(point-to-point)
-> 1:1 연결로 단일 송신측, 단일 수신측의 통신입니다.
신뢰적이고 순서가 유지된 Byte Stream
-> '메시지 경계'가 없습니다. 신뢰적이라는 말은 에러가 없다는 것을 보장하고 에러가 있다면 재전송한다는 의미이고, 연결을 하고 데이터를 흘려보내기 때문에 Byte Stream이라는 용어를 사용합니다.
파이프 라이닝(Pipelining)
-> TCP의 혼잡 제어 및 흐름 제어를 통해 윈도우 크기를 설정합니다. acknowledge 없이도 연속으로 데이터를 보낼 수 있지만 데이터는 최대 사이즈를 넘을수 없게 보냅니다. 여기서 데이터의 최대 사이즈를 윈도우라고 부르며 흐름제어에서 사용합니다.
전이중(full-duplex) data
-> 한 연결에 양방향 데이터를 전송합니다. (MSS: maximum segment size)
연결 지향(connection-oriented)
-> handshaking (제어 메시지의 교환)으로 송수신 측의 상태를 초기화한 후 데이터를 교환합니다.
흐름 제어(flow control)
-> 수신 측의 속도를 감안해 송신합니다.
혼잡 제어(congestion-control)
-> 망이 혼잡해지면 송신 측 속도를 낮춥니다.
제공하지 않는 것
-> 시간 보장, 처리율 보장, 보안
TCP 세그먼트 구조(segment structure)
source, dest port#
-> 각각 16-bit의 송신지와 수신지 포트넘버 입니다. 목적지 프로세스에 도착하려면 반드시 필요합니다.
sequence number(순서 번호)
-> TCP의 데이터 순서를 위한 순서 번호 입니다.
acknowledgement number
-> 데이터를 잘 받았는지 응답을 위한 데이터입니다.
head len
-> 헤더의 길이입니다. options가 있기 때문에 헤더의 길이가 바뀔 수 있으므로 존재합니다.
U(URG)
-> urgent 데이터. 긴급인지 아닌지를 나타내는 데이터이지만 현재는 사용되지 않습니다. TCP/IP의 설계는 미국 국방부에서 했기 때문에 군사용 용도로 포함된 헤더입니다.
A(ACK)
-> Acknowlodge 정보를 포함하고 있는지 나타내는 데이터.
P(PSH)
-> push data. 현재는 사용되지 않습니다.
R(RST), S(SYN), F(FIN)
-> TCP 연결/해제 할 때 사용되는 데이터입니다.
receive window
-> 한번에 얼마나 많은 데이터를 받을 수 있는지 나타내는 데이터입니다. 수신측이 얼마나 받을 수 있는지가 더 중요하기 때문에 수신측에서 데이터의 크기를 요청합니다. 데이터를 데이터의 전송 속도를 조절할 때 사용합니다
checksum
-> 데이터에 오류 검출을 위해 사용합니다.
options
-> 다양한 옵션들을 segment 데이터에 추가할 때 사용합니다.
application data
-> 어플리케이션 계층 데이터의 payload 입니다.
TCP의 순서 번호(Sequence number)와 응답(Acknowledgement) 설명
TCP의 header에는 sequence number와 ACKs(acknowledgements)가 존재합니다.
sequence number(순서 번호)
-> TCP는 데이터를 순서에 맞게 전달해야하기 떄문에 순서 번호를 제공합니다. (한번에 전송되는 숫자의 bytes의 갯수 단위로 전송합니다. ex) 데이터를 10개단위로 보낸다고 하면 1번째 데이터는 0, 2번째 데이터는 10이 됩니다.)
acknowledgements(응답)
-> 데이터를 잘 받았는지 응답을 위한 데이터이며 다음에 받아야할 데이터의 bytes number입니다. 예를들면, 이전에 데이터를 5바이트까지 받았다면 응답해야 할 acknowlegement 값은 6이 됩니다.
TCP 순서번호와 응답 과정
예를들어 위 그림을 보면, Host A가 Host B에게 C라는 데이터에 sequence number 42를 보내면 HostB는 acknowledge로 43을 보냅니다. 즉, acknowledge의 값은 받은 데이터가 아니라 보낼 데이터의 sequence number를 알려주는 것 입니다.
그래서 마지막에 Host A는 sequence number로 43번을 보내주게 됩니다.
그리고 위 그림에서는 데이터의 사이즈는 나와있지 않지만, sequnence number 42를 보냈는데 acknowledge가 43으로 응답이 왔다는 것을 통해 데이터의 사이즈가 1byte라는 것을 알 수 있습니다.
TCP의 RTT(round trip time)와 timeout 설정 과정
RTT(round trip time)
TCP에서는 데이터 전송 시 잘 받았다는 뜻으로 acknowlodge를 응답합니다. 그런데 만약, acknowlodge가 일정 시간안에 도달하지 않으면 문제가 생겼다고 판단하고 재전송을 하게 됩니다.
즉, TCP timeout 이라는 것은 어느정도를 기다렸을 때 acknowlodge가 도착하지 않으면 문제가 생겼다고 판단할 것인가를 말합니다.
Q. 그래서 TCP timeout 값을 어떻게 설정할 것인가?
RTT(round trip time)
-> 데이터가 전송되고 돌아올 때 까지의 왕복 시간
당연히 RTT 값 보다는 길게 설정을 해야 합니다.
but, RTT는 가변적입니다.
너무 짧으면 -> 불필요한 재전송이 발생하게됩니다.
너무 길면 -> 세그먼트의 손실에 늦게 반응하게 됩니다.
그래서 적절한 RTT 값을 찾는 것이 필요합니다.
Q. 그러면 RTT를 어떻게 추정할까요?
SampleRTT
-> 세그먼트 전송부터 ACK 수신 때까지의 측정된 시간이며 재전송은 무시합니다.
여기서 SampleRTT는 변하므로 조금 더 유연하게 추정하고 싶습니다.
-> 현재 SampleRTT 뿐만 아니라, 최근의 여러 측정값들을 평균을 냅니다.
그래프의 파란색이 최근에 측정된 RTT, 분홍색이 가중치를 적용한 이동 평균(moving average)를 측정한 것.
EstimatedRTT
= 기존의 EstimatedRTT + 최근에 측정된 RTT
지수적 가중 이동 평균(exponential weighted moving average)
-> 최근으로부터 얼마까지의 평균을 moving average라고 하는데 exponential weighted는 더 최근에 측정된 값을 더 가중치로 준다는 말입니다. 즉, 식에 있는 알파(a)라는 가중치를 SampleRTT에 부여한다면 기존의 EstimatedRTT에 (1-a)를 부여함으로써 기존에 있는 RTT는 가중치가 감소될 것입니다.
- 과거 샘플의 영향은 지수 함수적으로 감소합니다.
- 전형적인 권장값은 a = 0.125
rount trip time의 변이값을 반영해야 합니다.
timeout의 간격: 기존의 측정값(EstimatedRTT + 여유 값(safety margin)
- EstimatedRTT의 변동이 크면 -> 여유 값도 크도록 설정
SampleRTT의 EstimatedRTT에 대한 편차를 추정:
DevRTT(변동 RTT, 표준 편차)
= (1-B) * DevRTT + B * |SampleRTT - EstimatedRTT| (최근 측정 값 - 기존 측정 값) (B 값은 보통 0.25)
-> TimeoutInterval = EstimatedRTT(이동 평균으로 추정된 RTT) + 4*DevRTT(여유 값)
즉, 보통 여유 값의 4배로 설정합니다.
즉, timeout 값이 expired되었을 때 재전송을 요청합니다.
TCP의 신뢰적인 데이터 전달 방법
TCP와 UDP의 가장 큰 차이점은 TCP는 오류가 없을 때 까지 재전송하여 데이터 전달합니다.
TCP는 비신뢰적인 IP 서비스상에서 신뢰적 데이터 전달(rdt) 서비스를 제공합니다. (IP는 오류가 발생할 수 있으며 오류를 수정해주는 것을 보장하지 않으므로 TCP는 불완전한 IP에서 신뢰적인 데이터 전달을 해야하기 때문에 재전송이 중요합니다.)
- segment 파이프 라이닝(pipelining)
- 누적 확인 응답 (cumulative ACKs)
- 단일 재전송 타이머 (타이머가 expired되었을 때 재전송)
재전송 하는 경우
- timeout 발생 시(timout이 expired 될 때 까지 데이터가 전달되지 않을 경우)
- 중복된 ACK 수신 시(acknowlodge가 같은 데이터가 여러번 도착했을 경우)
우선 단순화된 TCP 송신자를 고려해보면
- 중복된 ACK 무시
- 흐름 제어, 혼잡 제어 무시
전송 계층에서 응용 계층의 데이터를 수신하게 되면
1. TCP의 헤더를 덧 붙여서 segment를 생성하고 순서 번호를 부여
2. 순서 번호는 segment 내 첫 데이터의 byte의 바이트열 번호
3. timer가 동작 중이 아니었으면 timer를 시작합니다.
- timer는 확인되지 않은 가장 오래된 segment를 위한 것
- 만료 주기: TimeoutInterval
timeout
- timeout이 발생하면 지정된 시간안에 ack가 도착하지 않은 것이므로 segment 재전송
- timer 재시작
ack가 수신되었고 ack가 확인 안된 segment를 확인 했다면
- 확인 상태 갱신
- 확인 안된 segment가 있다면 timer 다시 시작
TCP의 segment 재전송 시나리오
손실된 ACK 시나리오(왼쪽)
-> data 크기가 8bytes이고 sequence number가 92 이므로 acknowlodge 100이 와야하지만 오지 않고 timeout 되었으므로 다시 재전송을 합니다. (Host B 입장에서는 sequence number 92인 데이터가 중복된 데이터이므로 하나는 무시하고 재전송)
조기 timeout 시나리오(오른쪽)
-> sequence nubmer가 92이고 data의 크기가 8bytes인 데이터와 sequence nubmer가 100이고 data의 크기가 20bytes인 데이터가 연속으로 전송이 된 상황입니다. (파이프라이닝 기법을 사용하고 있기 때문에 acknowlodge가 도착하지 않더라도 연속으로 재전송 가능.)
여기서 문제는 두 데이터가 Host A에 도착하기는 했지만 timeout 된 후에 도착을 한 것입니다. 그래서 Host A는 Host B로부터 acknowlodge 응답이 올 것임에도 sequence number 92의 데이터의 재전송을 요청한 상황입니다.
이 상황을 조기 timeout이라고 합니다.
그러면 Host B는 중복된 데이터를 받게 되고 그때서야 자신이 이전에 보낸 acknowlodge가 timeout 이후에 도착했다는 것을 알게되고 중복된 데이터는 무시하고 acknowlodge 120을 보내서 해결하게 됩니다.
누적(cumulative) acknowledge
-> Host A에서 Seq 92인 데이터와 Seq 100인 데이터 두개가 전송이 되었습니다. Host B는 두개의 데이터를 모두 받았고 ack 100, 120을 보냈지만 ACK 100인 데이터는 손실이 되고 ACK 120만 Host A에 도착을 한 상황입니다. 이럴 경우 Host A는 비록 ACK 100인 데이터를 받지 못했지만 ACK 120인 데이터를 수신했으므로 그 앞의 데이터는 잘 받았다고 간주 할 수 있게 되고 다음 데이터인 Seq 120을 보내게 됩니다.
수신 측 이벤트 1
기대하는 순서 번호를 가진 순서가 맞는 segment가 도착. 기대하는 순서 번호까지의 모든 데이터들은 이미 ACKed 됨. (아무 문제 없이 잘 수신한 상태)
수신 측 동작 1
지연 된 ACK. 다음 segment를 500ms까지 기다려봄. 만약, 다음 segment가 오지 않으면 ACK를 보냄. (ACK를 바로 보내지 않고 지연 시키는 이유는 만약, ack를 보냈는데 segment 데이터가 그 사이에 도착한다면 ACK를 또 보내야 하므로 조금은 지연해서 보내는 것.)
수신 측 이벤트 2
기대하는 순서 번호를 가진 순서에 맞는 segment가 도착. 전송을 기다리는(pending) 순서에 맞는 segment가 하나 더 있음. (기다리는 순간에 다른 segment가 도착한 상태)
수신 측 동작 2
두 순서에 맞는 segment들을 ack를 하기 위해서, 즉시 누적된 segment를 전송. (두번의 ACK를 보낼 필요 없이 누적된 ACK를 보내면 됨.)
수신 측 이벤트 3
기대하는 순서번호보다 높은, 순서가 틀린 segment가 도착. 갭을 감지함. (1, 2 ,3 순서로 와야하지만 1, 3만 온 상태. 중간에 데이터가 누락된 상황이라 데이터의 Gap이 발생한 것임. 그러므로 동작에서 2를 다시 보내라고 ACK를 보내야 할 것임.)
수신 측 동작 3
기대하는 있는 바이트의 순서번호를 지닌 중복 ACK를 즉시 전송 (해당 손실된 ACK를 다시 전송하므로 중복 ACK를 전송하는 것임. 송신측에서는 중복된 ACK를 받았다는 것은 재전송하라는 의미이므로 재전송을 함.)
수신 측 이벤트 4
갭을 부분적으로 또는 완전히 채우는 segment 도착(1, 2, 3이 와야하는 상태에서 1, 3이 먼저 도착해서 2의 재전송을 요청했는데 2가 나중에 도착한 상태.)
수신 측 동작 4
갭의 중간이 채워졌을 경우, 즉시 ACK 전송(전부 다 받은 것을 확인했으므로 마지막 ACK를 전송. 즉, 1, 2, 3 모두 정상적으로 받은 것을 확인했으므로 ACK 4를 보내야 할 것임.)
TCP 빠른 재전송(fast retransmit)
timeout 주기는 상대적으로 길다. (긴 시간동안 기다렸다는 뜻)
-> 손실된 패킷을 재전송하기까지의 지연이 길어짐.
중복된 ACK
를 통해 손실을 감지(중복된 ACK를 받았다는 것은 데이터의 손실이 있다는 뜻. timeout이 없어도 재전송 해야함.)
-> 송신 측은 자주 여러 segment들을 연속으로 전송
-> segment가 손실되면 중복 ACK를 받을 것임.
그러므로 만일, 같은 데이터에 대해 ACK를 3번 받으면(ACK가 3번 중복되면), ACK 안된 segment 중 가장 작은 sequence #의 데이터를 재전송합니다.
-> 손실 되었을 가능성이 높으므로 timeout을 기다리지 않음.
(timeout이 여러번 발생하는 것보다는 ACK를 3번 받는 것이 상대적으로 덜 심각한 네트워크 상태입니다. 왜냐하면 timeout은 아예 응답조차 오지 않은 것이기 때문입니다)
TCP 빠른 재전송(fast retransmit) 예시
위의 그림은 seq 92번의 응답은 제대로 도착했지만, seq 100의 응답은 제대로 도착하지 않은 상태.
그래서 100번의 재전송을 3번 요청했는데 그 후 중복된 ACK 100의 응답이 도착한 상태입니다.
3번의 중복이 발생했다라는 것은 단순한 문제가 아니라 네트워크의 문제(ex. 혼잡상황)
가 있을 가능성이 큽니다.
그러므로 네트워크 문제를 해결해야할 가능성이 높습니다.
흐름 제어(flow control)
흐름 제어(flow control)
는 송신측이 수신측을 제어해서, 수신측이 너무 빨리 보내서 송신측의 버퍼가 넘치지 않게 해주는 기법입니다. (수신측의 능력에 맞게 송신측의 데이터 속도를 조절해야 함.)
흐름 제어가 필요한 이유
만약, TCP socket의 버퍼(buffer)에 쌓이는 속도가 가져가는 속도보다 빠르다면 결국 나중에는 buffer가 꽉차서 더 이상 저장할 수 없기 때문에 데이터 손실(loss)가 생길 것입니다. 그래서 흐름 제어를 통해 송신측의 데이터를 전달하는 속도를 조절해서 TCP socket의 버퍼에 쌓는 속도가 너무 빠르지 않도록 조절하는 것 입니다.
TCP 흐름제어의 원리
* 보시려면 더보기를 누르세요
위 그림을 보면, 수신 측에서 데이터 전송을 받을 때 데이터가 네트워크 계층 -> 전송 계층으로 올라갈 때 TCP socket receiver buffers가 존재합니다. 여기서 버퍼(buffer)란 받은 데이터를 담는 저장소입니다. 그리고 버퍼에 쌓여있는 데이터를 응용 계층에서 가져가는 것입니다. (e.g. HTTP, SMTP 프로토콜이 TCP 소켓 버퍼에 쌓여있는 데이터를 가져감)
응용 계층의 프로토콜이 TCP socket의 버퍼에 있는 데이터를 가져가는 속도가 있고 버퍼에 쌓이는 속도가 존재할 것입니다.
만약, 버퍼에 쌓이는 속도가 가져가는 속도보다 빠르다면 결국 나중에는 버퍼가 꽉차서 더 이상 저장할 수 없기 때문에 데이터 손실(loss)가 생길 것입니다.
반대로, 응용 계층에서 가져가는 속도가 버퍼에 쌓이는 속도보다 빠르다면 점점 버퍼가 고갈이 되고 버퍼에 아무런 데이터가 없는 상황이 생길 것입니다. 즉, 응용 계층에서 새로운 데이터를 기다리는 상황이 생깁니다.
-> 즉 흐름 제어
란, 송신측의 데이터를 전달하는 속도를 조절해서 TCP socket의 buffer에 쌓는 속도가 너무 빠르거나 느리지않게 조절하는 것.
흐름 제어는 receiver의 window size를 조절해서 송신측의 데이터 속도를 조절합니다.
(window size란 송신측에서 ACK 없이 한번에 보낼수 있는 데이터의 양을 말합니다.)
위 그림에서 RcvBuffer는 전체 버퍼 공간, rwnd는 비어있는 버퍼 공간입니다.
수신측이 보내는 TCP 헤더의 rwnd 값을 통해서 비어있는 버퍼 공간을 알려줄 수 있습니다.(즉, rwnd 값을 늘리면 속도가 빨라지고 줄이면 속도가 느려집니다.)
-> RcvBuffer의 크기는 소켓 옵션으로 설정합니다.(전형적인 default 값은 4096 bytes) 네트워크가 점점 빨라지면서 버퍼 공간의 크기는 4096bytes가 아니라 점점 크기를 늘리는 추세이고 소켓 프로그래밍을 할 때 이 사이즈를 늘릴 수 있습니다.
-> 많은 운영체제가 RcvBuffer를 자동 조절합니다.
송신 측은 ACK 안된 데이터의 양을 수신측의 rwnd 값으로 제한합니다.
수신 측 버퍼가 넘치지 않도록 보장합니다.
Sliding Window
-> TCP에서 window size를 통해서 흐름제어 하는 기법.
TCP 연결 - handshake란?
TCP는 connection-oriented 프로토콜이기 때문에 데이터 전송 전에 연결을 설립하는 핸드쉐이크(handshake)
과정을 수행합니다.
즉, TCP에서 데이터 교환 전에, 송/수신 측이 연결 조건(connection parameters)에 합의 하는 과정
여기서 핸드쉐이킹은 2-way handshake와 3-way handshake가 두 가지가 존재합니다.
2-way handshake의 단점
- 지연 시간이 가변적입니다.
- 메시지 손실 때문에 메시지를 재전송할 수 있습니다.(e.g. req_conn(x))
- 메시지 순서가 바뀔 수 있습니다.
- 상대방의 상황을 볼 수 없습니다.
-> 서버가 클라이언트에게 클라이언트의 연결 설립 응답 메시지를 보냈다고 하더라도 클라이언트가 응답을 잘 받았는지 알 수 없습니다. 그래서 2단계의 핸드쉐이킹으로는 양쪽의 connection 설립을 확인할 수 없고 이러한 단점 때문에 3-way handshaking을 많이 사용합니다.
3-way handshake
3-way handshake
는 2-way 핸드쉐이킹 과정에서 서버에 대한 클라이언트의 응답과정을 추가하여 서버의 응답을 잘 받았는지의 과정을 추가합니다. 이렇게 하면 클라이언트, 서버 측에서 모두 연결 설립이 되어있는지 알 수 있습니다.
3-단계 핸드쉐이킹(3-way handshake) 과정
* 보시려면 더보기를 누르세요
1. 클라이언트가 서버에게 establish connection을 요청 하는데 이 때, SYNbit = 1로 설정해서 보냅니다. (여기서 SYNbit란 TCP segment header에 있던 필드. SYNbit 1이라는 것은 connection 설립을 요청하는 것을 말합니다.)
2. 서버는 클라이언트에게 SYNbit에 대해서 ACKbit를 보내고 클라이언트는 응답 요청을 받아들였는지 확인할 수 있습니다. 여기서 클라이언트는 서버의 응답 과정에서 이미 ESTAB를 확인할 수 있습니다. 하지만 서버는 SYNCbit에 대한 응답만 보냈을 뿐 클라이언트가 받았는지 알 수 없습니다.
3. 그래서 클라이언트가 서버에게 ACKbit를 보내서 서버가 클라이언트에게 보낸 자신의 응답메시지를 클라이언트가 잘 받았는지 확인할 수 있습니다.
위 과정과 같이 클라이언트, 서버 측에서 모두 연결 설립이 되어있는지 알 수 있는 과정을 3-way handshake라고 합니다.
TCP 연결 닫기
TCP 연결 닫기는 데이터 전송까지 마쳤을 때 4-way handshake를 해서 연결을 해제하는 과정입니다.
클라이언트, 서버는 각자 자기 쪽의 연결을 닫습니다. (연결할 때는 3-way handshake를 통해서 서로 합의하에 한번에 설립했지만 연결을 해제할 때는 양방향 통신이기 때문에 동시에 각자 자기쪽에서 전송되는 connection을 닫아도 됩니다.)
TCP 연결 닫기 과정
* 보시려면 더보기를 누르세요
연결을 해제하는 과정은 처음에 클라이언트, 서버 모드 establish connection이 된 상태이므로 ESTAB 상태에서 시작합니다.
1. 클라이언트가 서버에게 연결을 해제하자고 FINbit를 보냄
(FIN bit는 연결을 해제하자는 의미입니다.)
-> 클라이언트의 FIN_WAIT_1 상태는 클라이언트가 서버에게 연결해제 요청을 했지만 응답을 받지 않은 상태
-> 서버의 CLOSE_WAIT 상태는 클라이언트로부터 연결해제 요청을 받은 상태
2. 서버가 연결을 해제에 동의하고 ACK를 응답
(클라이언트에서 서버에게 전송하는 연결의 해제가 된 상태. 서버는 can still send data)
-> 클라이언트의 FIN_WAIT_2 상태는 한쪽방향의 연결은 해제가 됐다는 것을 의미
3. 서버가 클라이언트에게 연결을 해제하자고 Finbit를 보냄
-> 클라이언트의 TIMED_WAIT 상태는 서버의 연결 해제를 요청 받은 상태
-> 서버의 LAST_ACK 상태는 클라이언트에게 연결 해제 요청을 보낸 상태입니다.
4. 클라이언트가 연결을 해제에 동의하고 ACK를 응답
(서버가 클라이언트에게 보내는 전송하는 연결을 해제. 그 후, 클라이언트에서 서버에게 전송하는 연결도 해제됩니다.)
-> 이 과정에서 클라이언트는 timed wait 과정이 있는데 2번 단계에서 서버가 요청한 연결 종료에 대해서 요청은 했지만 요청 데이터가 손실될 가능성이 있으므로 클라이언트가 ACK를 재전송해야 할 가능성이 있으므로 기다리는 과정입니다. 클라이언트가 서버가 종료되는지 기다리는 이유입니다.
혼잡 제어(congestion control)
혼잡 제어(congestion control)
는 네트워크의 혼잡 상황을 해결하기위한 기법입니다. 여기서 혼잡 상황이란, 네트워크 traffic이 엄청나게 늘어나서 네트워크가 데이터를 처리하기에는 데이터 전송 속도가 너무 빠른 상태를 말합니다.
혼잡상황이 발생할 때의 소견(manifestation)
- lost pacakets
-> buffer에 데이터를 보관할 공간이 없어서 데이터 손실(loss)
- long delay
-> buffer에서 대기시간이 증가함. (queueing delay 증가)
-> 혼잡상황을 해결할 수 있는 방법은 송신측의 전송 데이터의 속도를 줄여서 트래픽을 줄이는 한 가지 방법밖에 존재하지 않습니다. 그래서 window size를 줄여가며 해결해야 합니다. 다만, window size를 너무 줄이면 TCP의 성능에 문제가 생기므로 Slow Start라는 기법을 사용합니다.
Slow Start
-> cwnd(congestion window)의 크기를 처음에는 천천히 늘리지만, 나중에는 기하급수적으로 늘려보는 방법. (하지만 어느정도 수준까지 올라가면 additive increase, linear increase로 변경) 이 기법을 사용하면 cwnd에 대한 그래프가 마치 톱날 모양(saw tooth)과 같은 모양이 형성됩니다.
혼잡 제어의 원리
혼잡 상황 시나리오
왼쪽 그래프 -> x축은 송신측이 보내는 데이터를 의미하고, y축은 패킷이 수신측의 buffer를 빠져나와서 응용 계층으로 나가는(out) 패킷을 의미합니다. 일정순간(R/2) 부터는 패킷이 빠져나가지 못하는 혼잡 상황을 의미합니다.
오른쪽 그래프 -> x축은 송신측이 보내는 데이터를 의미하고, y축은 지연 시간(delay)를 의미합니다. 일정순간(R/2) 부터는 지연 시간이 기하 급수적으로 커지는 혼잡 상황을 의미합니다.
-> 혼잡상황을 해결할 수 있는 방법은 송신측의 전송 데이터의 속도를 줄여서 트래픽을 줄이는 한 가지 방법밖에 존재하지 않는다. 즉, window size를 줄어야 합니다.
혼잡 제어
는 데이터 전송률을 늘리고 줄이면서 적절한 cwnd를 결정합니다.
이 때, traffic(window size)을 늘릴 때는 데이터 손실이 크게 일어나면 안되므로 천천히 늘리고, 감소시킬 때는 급격하게 감소시킵니다.
접근: 송신측에서 데이터 손실이 발생할 때까지 전송 가능한 대역폭을 검색하면서 전송 속도를 높입니다.
-> 조심스러운 증가(additive increase)
: 데이터 손실이 감지될 때까지 cwnd를 1씩 증가시킵니다.
-> 급격스러운 감소(multiplicative decrease)
: 데이터 손실 후 cwnd를 반으로 줄입니다.
마치 톱날 모양(saw tooth)이 TCP의 혼잡 상황 그래프의 모양입니다.
혼잡상황을 감지하는 경우의 수는 두 가지
1. timeout이 계속 발생(트래픽이 심각하다는 것)
2. ACK가 중복적으로 도착(그나마 ACK가 도착은 하니까 timeout보다는 덜 심각한 상태)
Slow Start
Slow Start
는 처음 비율은 천천히 시작하지만, 기하급수적으로 늘려보는 방법. (하지만 어느정도 수준까지 올라가면 additive increase로 변경)
처음 connection이 시작되고, 첫 데이터 손실까지 기하급수적으로 cwnd를 늘립니다.
- 첫 cwnd = 1로 설정
- 그리고 각각 cwnd마다 두 배씩 늘립니다.
- cwnd를 증가시켜서 모든 ACK 응답을 완료.
혼잡상황 감지와, 데이터 손실에대한 반응
- timeout으로 인해 나타난 손실 (데이터가 오지 않았으므로 네트워크 상태가 심각한 상황에 놓여있는 상태)
-> cwnd를 1로 설정합니다.
-> 그 후, window size를 기준점까지(threshold) 급격하게 늘리고, 그 후, 선형으로(천천히) 늘립니다. (마치 slow start)
3번의 ACK 중복으로 인해 나타난 손실: TCP RENO(좀 더 개선되었음)
-> ACK 중복은 네트워크가 segment를 전달할 수 있음을 나타냅니다. (timeout보다는 traffic이 덜함)
-> cwnd를 절반으로 줄이고 선형으로 증가시킵니다.
TCP Tahoe(TCP 초기 버전의 이름)는 항상 cwnd를 1로 설정합니다 (timeout 혹은 3번의 ACK 중복)
Tahoe -> RENO -> Vegas 버전은 순
Q. 어느때 부터 cwnd를 급격한 증가에서 선형 증가로 바꾸어야 하는가?
A. cwnd가 이전 timeout의 절반에 도달했을 때.
그림에서 timeout이 발생하면 TCP Tahoe는 cwnd를 무조건 1로 감소 시키고, TCP Reno는 cwnd를 절반으로 줄입니다.
구현:
- ssthresh 변화 (데이터 손실이 언제 발생했느냐에 따라 다시 설정.)
- 데이터 손실 이벤트가 발생하면, ssthresh를 이전 데이터 손실 이벤트가 발생했을 때의 cwnd 절반으로 설정합니다.
References
Computer Networking: A Top-Down Approach