목차
1. Overview
2. TCP Sequence number와 ACKs
3. TCP Sender event
4. TCP Fast Retransmit
5. TCP Timeout and RTT
1. Overview
- Point-to-point
- 하나의 소켓에는 하나의 서버와 클라이언트가 연결되어 있다.
- 즉, process와 process의 1:1 통신이다.
- Reliable, in-order byte stream
- No "message boundaries"
- 메시지 경계가 없다는 게 뭔가 했더니 StackOverFlow에 답이 있었다. (역시...)
- UDP는 "FOO", "BAR"라고 보내면 그대로 딱딱 맞게 가야하지만, TCP는 뒤섞여서 보내진다. (어차피 seq#로 재조립 가능하기 때문)
- Full duplex data
- MSS: maximum segment size (일반적으로 MTU와 사이즈가 같다)
- Cumulative ACKs
- Go-back-N과는 조금 다른 개념이다.
- cumulative ACK이 아니라, 내가 다음으로 받아야 하는 seq#을 보낸다.
- Pipelining
- Sliding window
- TCP는 window size를 이용해서 congestion & flow control을 한다. (Internet 속도에 직접적인 영향)
- congestion control : sender가 network 혼잡도에 따라 전송량을 조절한다.
- flow control : receiver가 sender의 전송량을 통제한다.
- Connection-oriented
- 3-way handshaking을 먼저 수행한다.
- 서로 시작할 seq #와 window size 정보를 주고 받아 초기화한다.
- TCP Segment Structure (20byte)
필드 명칭 | 길이(bit) | 설명 |
source port # | 16 | 패킷을 송신한 측의 프로세스 포트 번호 |
dest port # | 16 | 패킷을 수신한 측의 프로세스 포트 번호 |
seq # (송신 데이터의 일련 번호) | 32 | 패킷의 맨 앞에 위치한 데이터가 송신 데이터의 몇 번째 바이트에 해당하는지 송신측에서 수신측에 전달하기 위한 필드 |
ACK # (수신 데이터의 일련 번호) | 32 | 데이터가 몇 바이트까지 수신측에 도착했는지를 수신측에서 송신측에 전달하기 위한 것 |
data offset | 4 | 데이터 부분이 어디부터 시작하는지. (헤더 길이와 동일) |
not used | 6 | 사용하지 않음 |
control bit | 6 | 각각 통신 제어상의 의미를 가진다. • URG : 긴급 포인터 필드가 유효함 • ACK : 수신 데이터 일련번호 필드가 유효함 (수신 양호) • PSH : flush 동작에 의해 송신된 데이터 • RST : 접속을 강제 종료하고 이상 종료시 사용 • SYN : 송신측과 수신측에서 일련번호 서로 확인. (접속 동작) • FIN : 연결 끊기 |
window | 16 | 수신측에서 송신측에 window size 통지 |
checksum | 16 | 오류 유무 검사 |
Urg data pointer | 16 | 긴급하게 처리해야 할 데이터 위치 표시 |
options | 가변 | 위의 헤더 필드 이외의 제어 정보 기록하기 위함. |
→ src port #, dest port #, seq #, ack #, receive window 확인 (내 시험을 위해..)
2. TCP Sequence number와 ACKs
- Sequence number : Segment의 첫 번째 byte의 byte stream number (init seq #는 해킹에 대비하여 랜덤값)
- ACK : 다음으로 받아야 하는 byte 순서 번호
여기서 다음 받을 byte 번호가 43인 이유는 A로부터 seq# 42와 1byte 문자 'C'가 왔기 때문이다.
즉, 1byte 증가했으므로 다음 받을 seq#는 43이 된다.
ACK과 data를 한 번에 보내는 방식을 Piggy back이라고 한다.
3. TCP Sender event
1️⃣ Application으로 부터 data 수신 (rdt_send)
- seq#를 가진 segment 생성
- seq#은 첫 번째 byte의 byte stream number다.
- 다른 segment에 의해 timer가 실행 중이 아니라면 timer 시작
2️⃣ Time-out
- time-out을 일으킨 segment를 재전송한다.
- timer을 재시작한다. (send_base)
3️⃣ 확인되지 않은 Segment의 ACK received
- ACK의 seg#가 send_base보다 크면 이전에 확인 응답이 안 된 segment에 대해 확인하고 send_base 갱신
- 아직 확인 응답 안 된 segment가 존재하면 timer 재시작
📌 여러가지 재전송 시나리오
왼쪽의 경우 ACK(100)이 오다 손실되면 Seq(92)에서 TimeOut이 발생한다.
그러면 Sender는 다시 92부터 8byte를 재전송한 후에 ACK(100)을 받으면 send_base를 100으로 옮기면 된다.
오른쪽의 경우 ACK(100)은 손실되고 ACK(120)이 들어온다.
TCP에서 Receiver는 Cumulative ACK을 사용하므로 비록 ACK(100)은 못 받았지만, seq#119까지 잘 받았다는 걸 알 수 있다. 따라서 Seq#120번에 대한 데이터를 전송하고 TimeOut은 발생하지 않는다.
두 개의 packet이 늦게 와서 timeout이 발생하긴 했는데, 재전송 하자마자 ACK(100), ACK(120)을 수신하면서 send_base를 120으로 옮겨버린다.
이 다음부터는 120번부터 나가고, 재전송한 packet에 대한 ACK(100)은 무시한다. (이미 window sliding을 함)
3. TCP Fast Retransmit
TCP가 재전송하는 경우는 두 가지가 있다.
- Packet Loss가 발생해서 time out이 난 경우
- time out과 상관없이 중복 ACK을 3번 받은 경우
bit error는 요즘 기술력으로는 거의 발생하지 않는다고 한다.
Sender는 중복된 ACK을 3번 이상 받으면 time out을 기다리지 않고 재전송 해버린다.
이 정도면 "아, 뭔가 잘못 됐구나" 싶은 상황이라, 굳이 time out 날 거 기다리지 말고 한 번 더 보내버리는 게 통신 관점에서 이득이라고 한다.
이 개념이 나중에 Congestion Control에 또 나오니 기억해두자!!
난 일단 시험을 위해서라도..!!
4. TCP Timeout and RTT
이전에 #7쯤에서 신뢰할 수 있는 가상의 통신 프로토콜을 정의하던 중 timer를 정할 때는 "합리적인" 시간으로 한다고 했다.
어떻게 정해야 합리적이라고 할 수 있을까?
- Time too short : premature timeout과 같은 불필요한 재전송이 발생한다.
- Time too long : Segment loss에 대한 응답이 늦어진다.
이 문제를 해결하기 위해서는 RTT를 측정해야 한다.
📌 RTT 설정
- Sample RTT
- Sender가 어떤 packet을 보내고 응답이 돌아올 때까지 매번 시간을 확인한다.
- 실제로 측정을 해보면 굉장히 변동이 심하기 때문에 기본적인 RTT 값으로 사용할 수 없다.
- Estimated RTT
- Estimated RTT는 Sample RTT의 가중 평균 값이다.
- \( EstimatedRTT = (1-\alpha)*EstimatedRTT + \alpha*SampleRTT \)
- EWMA (Exponential weighted moving average)
- CPU scheduling에 나오는 burst time과 비슷한 개념
- SampleRTT 가중치 α를 0.125로 적용한다.
- 측정값 history를 활용하나, 최근 측정값에 더 가중치를 둔다.
- Timeout은 Estimated RTT 기준으로 정한다. 그래서 Timeout은 Estimated RTT보다 약간 크게 잡아야 한다.
📌 Timeout 설정
- Time out interval : Estimated RTT보다 약간의 여유값(Dev RTT)을 더한 값으로 설정한다.
- \( TimeoutInterval = EstimatedRTT + 4*DevRTT \)
- 4*DevRTT는 "safety margin"으로 SRTT 표준편차*4에 해당한다.
- Dev RTT : Sample RTT가 Estimated RTT로부터 얼마나 벗어나는지에 대한 예측. (network가 얼마나 불안정한가)
- \( DevRTT = (1-\beta)*DevRTT + \beta*|SampleRTT - EstimatedRTT| \)
- β = 0.25