이제 TCP에 대해서 자세히 알아볼 시간이다.
TCP Overview
- One-to-one point
- 한명의 Sender와 Receiver 사이에서 데이터가 교환
- Reliable , in-order Byte stream
- 패킷 단위로 데이터를 구분하지 않고 Byte 단위로 구분
- 데이터를 끊어서 메시지로 처리하는지는 application layer 담당임.
- full duplex data
- 양방향 통신 지원 : TCP 연결을 통해 양쪽에서 동시에 데이터 전송 가능
- MSS : maximum segment size 존재 (전송되는 segment의 최대 크기를 제한하는 term)
- Cumulative ACKs
- 누적 ACK를 보냄 : 그 전의 모든 데이터는 수신 완료
- Pipelining
- congestion / flow control set window size
- Connection-oriented
- Data exchange 전 hand-shaking으로 미리 연결 수립해야함
- Flow-control
- 한꺼번에 너무 많이 보내기 금지
TCP segment structure
- TCP segment는 위와같이 생김
- Source port / dest port #는 그냥 송신 / 수신 포트번호
- Sequence number field세그먼트에 대한 순서 번호는 세그먼트에 있는 첫 번째 바이트의 바이트 스트림 번호다.
- 데이터 스트림은 500,000 바이트로 구성된 파일이라고 가정한다.
- MSS는 1,000 바이트
- 데이터 스트림의 첫 번째 바이트는 0으로 설정한다.
- EX)
- TCP는 데이터를 구조화되어 있지 않고 단지 순서대로 정렬되어 있는 바이트 스트림으로 본다.
- Acknowledgement field
- TCP는 full duplex data이므로, 양방향 통신이 가능하다.
- 이때 Host B로부터 도착한 segment는 B→A로 들어온 데이터에 대한 sequence number을 갖는다.
- A→ B에게 데이터를 전송하는 과정에서, B도 A에게 데이터를 보낸다고 가정해보자.
- 이 방식은 cumulative ACK 이용
- EX) A가 B로부터 0~535에 해당하는 segment와 900~1000에 해당하는 segment를 받았다면
- A의 다음 Ack field에는 536이 써져있음
- 이 상황에서는 , 순서가 잘못됐음 ( 1→3→2)
- 이때 TCP official은 어떻게 하라고 정해놓진 않았고, TCP 구현 개발자들이 알아서 처리하게 만들어놓음
- 보통 Discard / buffer에 넣고 기다리기 둘 중하난데, 후자 대부분 이용
이 과정을 간단한 telnet 시나리오를 통해 살펴보면, 다음 그림과 같다.
TCP round trip time, timeout
→ TCP는, timer를 통해서 ARQ를 구현한다고 했다. 그럼이 Time은 어떻게 측정하는 것일까?
- 너무 짧으면 불필요한 재전송이 많음
- 너무 길면 느림
Remark)
💡 왕복 시간(round-trip time, RTT) : 세그먼트가 전송된 시간부터 긍정 확인응답될 때까지의 시간
- How to estimate RTT?
- SampleRTT
- Segment가 송신된 시각(IP에게 넘겨진 시각)으로부터 그 segment에 대한 Ack대답이 돌아오기까지의 시간
- 대부분의 TCP는 한번에 하나의 SampleRTT 측정
- SampleRTT는 네트워크 상태마다 달라질 수 있으므로, 과거 측정값 몇개를 이용해 평균을 내서 이
- 용
- SampleRTT
- EWMA (Exponential Weighted Moving Average)
- 이떄, 가중치 a를 설정해 현재와 과거에 대한 비중을 설정할 수 있음.
- a가 크면, 새로운 측정값이 영향을 미치는 상황 = 네트워크 상태가 빠르게 변할 때 이용 가능
- a가 작으면, 기존 측정값이 영향을 미치는 상황 = 변동이 적어 안정적인 RTT 추정 가능
- a는 보통 1.25 사용
→ 이렇게 RTT를 추정한 RTT를 적용
→ But delay 때문에 RTT가 부족해 정상적으로 작동하지 않는 경우가 발생할 수 있음.
→ 이 상황을 대비해서, DevRTT ( Safety margin)을 적용해 안전빵으로 시간 확보
DevRTT로는, sample과 E의 절댓값에, 가중치를 곱한 값으로 사용 (보통 0.25)
Reliable Data transfer
- 지금껏 나왔던 Sender 입장에서의 TCP의 event → action을 정리해보면, 다음과 같다.
- Data recived from application
- sequence #에 해당하는 segment 만들고, (B-stream #)
- timer가 동작중이 아니면 timer 시작
- timer는 가장 오래된 unACKed segment 기준
- timeout interval = 위의 방식으로 구함
- Timeout
- Timeout을 유발한 패킷 재전송
- Timer 재시작
- ACK received
- 만약 ACK가 이전에 unACKed였던 segment에 대한 거라면
- Sender은 ACked data update
- 여전히 ACKed 안됐다면, ( 중복 ACK ) 타이머 재시작
- 만약 ACK가 이전에 unACKed였던 segment에 대한 거라면
- 그렇다면, 이제 Receiver 입장에서의 TCP event→action을 생각해보자.
- In-order seq #를 가진 segment가 도착 : seq #이전의 모든 segment들이 이미 ACKed
- → dalayed ACK 실행 : 500ms 기다렸다가 다른 segment 없으면 ACK 응답
- In-oder seq #를 가진 segment가 도착 : seq # 이전의 segment에 대해서, 아직 buffer에 남아있는 것이 존재 ( 과거엔 out-of-order이라 버퍼에 보관해놨지만 지금은 In-order seq#와 연속적인 값을 가지고 있어, 보내야하는 값들 )
- → cumulative ACK 실행 : 2개의 ‘순서가 맞는’ 세그먼트들을 ACK하기 위해, 하나의 누적된 ACK를 즉시 보낸다.
- out-of-order seq #를 가진 segment가 도착 ( 이 #는 expected #보다 높음 )
- 이는 expected byte의 seq #를 뜻함. ( 밑에서 설명 )
- ⇒ 즉시 duplicated ACK 전송
- 수신 데이터에서 격차를 부분적으로 또는 모두 채우는 세그먼트의 도착
- 세그먼트가 격차의 최솟값부터 시작하면 즉시 ACK를 보냄
- Receiver가 duplicate ACK를 보내는 이유
TCP는 부정 확인응답을 사용하지 않으므로, 수신자는 송신자에게 부정 확인 응답을 보낼 수 없다.
→ 그냥 받아야할거 달라고 계속 떼쓰면 됨.
TCP fast retransmit
우선, TCP가 중복해서 전송되는 상황을 생각해보자.
위와 같은 2번의 상황에선, retransmit이 발생한다
- 그럼, 이 상황에선 어떨까? ACK 100이 중간에 loss되었고, B는 A에게 ACK(120)을 응답하엿다.
- ACK 1개가 유실되었음에도 불구하고, 문제는 없다. 오히려, ACK(120)은 ACK(100)도 포함하기에, ACK(120)만 보내는 것이 효율성 측면에서 높다.
- → 이것이 ACK를 보내기 전에 500ms를 기다리는 이유이다.
그런데, 다음과 같은 상황이 발생할 수 있다. 많은 수의 duplicate ACK가 Timeout이 끊나기 전 Sender에게 여러번 도착할 수 있다. 그런데, Sender은 다시 전송하려면 timeout이 끝날때까지 대기해야하는 상황이 발생한다.
이런 경우를 해결하기 위해서, “3번의 duplicate ACKS가 돌아오면, timeout을 고려하지 않고 즉시 데이터를 receiver에게 재전송해주는 Fast retransmit” 기법을 사용한다.
TCP flow control
이렇게 TCP들이 데이터를 주고받을 때, socket buffer에는 application layer에 데이터를 보내기 위해 데이터를 버퍼에 저장해놓는다.
그런데 만약, application layer가 socket buffer를 비우는 속도보다 더 빠른 속도로 network layer에서 data를 전달해주면 어떡할까?
→ 이를 방지하기 위해서, flow control이 필요하다.
💡 TCP는 수신 윈도(receive window)라는 변수를 유지하여 흐름 제어를 제공한다.
앞서, TCP segment의 구조를 살펴볼 때, 중간에 receive window가 있었다. 이는
- 수신 측에서 가용한 버퍼 공간이 얼마나 되는지를 송신자에게 알려주는데 사용된다.
- TCP는 전이중(full-duplex)이므로 연결의 각 측의 송신자는 별개의 수신 윈도를 유지한다.
- TCP 수신자 측에서는, 자신이 보유한 free buffer space의 크기를 rwnd field에 담아 송신자로 보낸다
- 이때 RcvBuffer의 사이즈는 소켓마다 다른데, 보통 4KB이고, 조정 가능하다 (개인에 맞게 써라)
- 이 rwnd fields는 TCP header에 포함되어 있다.
- Sender은 아직 data를 rwnd 크기만큼 제한해서 전송한다. 이를 in-flight data라고 부른다.
→ 이러한 과정들은 overflow가 발생해 패킷이 손실되지 않아, reliability를 유지할 수 있게 해준다.
TCP connection management
- TCP는, data exchange 전 먼저 ‘handshake’과정을 거친다.
- 서로 connection에 동의하는 과정이다.
그렇다면, 이 handshake과정을 어떻게 수립할 수 있을까?
사람처럼, 2-way handshake 과정을 생각해보자.
위와 같은 상황엥서는, A가 B에서 연결을 제안하고, B는 응답으로 OK를 보내는 상황이다.
보기엔 타당해 보이지만, 다음과 같은 문제상황이 생길 수 있다.
- 한명이 connection 응답을 보내는 동안, TCP 연결이 해제되어, 서버 입장에서는 어? 새로운 TCP 연결 시도인가? 라는 생각을 가지게 돼 half open conneciton이 발생한다.
- delay 때문에 데이터 전송이 느려지는 동안, 2개의 data를 받을 수 있다. ( not reliable : 1개당 1개씩 받아야함)
- 이는 1단계의 handshaking 과정을 추가하면 해결할 수 있다.
- 이는 바로 SYN 단계이다.
- 위의 3-way handshaking 과정을 설명하면 다음과 같다.
- 클라이언트는 initial seq #를 정하고, SYN 을 1로 설정한 후 server로 보낸다.
- LISTEN 상태 ( 연결 대기상태) → SYNSENT 단계로 진입
- 이때 LISTEN 상태이던 서버는 클라이언트로부터 SYN 패킷을 받으면, 자신의 초기 seq # y와 SYN/ACK 응답을 보낸다.
- 서버가 client의 SYN 요청을 ACK하며, 자신의 SYN 비트와 seq#인 y를 포함한 패킷을 보내고, SYS_RECEIVED 상태가 된다.
- 당연히 ACK # = x+1이겠쥬
- Client는 SYNACK이 담긴 segment를 받고, ESTABLISH 상태로 진입한다. 이때 서버의 SYN을 동의한다는 의미로 ACK bit을 set한채 server에 전송한다. ( SYN은 이미 연결이 성립되었기에 0임)
- 서버는 ACK bit을 확인하고, ESTABLISH 상태에 진입한다.
정리하자면, Client→ server : 야 나랑 친구하자
server →client: 좋아 / 너도 나랑 친구할래?
client → server : 좋아
== 3-way handshake
그렇다면, 이 연결을 끊을 때에도 3-way로 동작할까? 그렇다
아래 그림은 TCP connection을 closing할 때의 과정이다.
- 먼저, EST 상태에 있던 client는 server에 FIN bit을 set한채 요청을 보낸다.
- 데이터를 보낼 순 없지만, 받을 수는 있는 상태이다.→ FIN 요청을 ACK할때까지 FIN_WAIT_1 상태에 진입한다
- Server은, ACKbit을 set한채로 응답을 보낸다. 이후, CLOSE_WAIT 상태에 진입한다.
- 이후, 서버도 FIN bit을 set한채로 Close 요청을 보낸다.
- 이떄 client는 FIN_WAIT_2 상태로 진입한다.
- 이때 서버는 아직 닫는 것에 대한 ACK을 한번 더 못받았기 때문에,
- 하지만 FIN bit을 전송한 후에는 data 전송이 불가능하다.
- ACK bit 전송 과 FIN bit 전송 사이의 period에는 여전히 데이터 전송이 가능하다.
- 이후, 서버도 FIN bit을 set한채로 Close 요청을 보낸다.
- Client는, FIN_WAIT_2 상태에서 server의 FIN bit을 기다리다가, FIN bit이 오면, 응답으로 ACK bit이 포함된 세그먼트를 보내고, TIME_WAIT 상태로 진입한다.
- 이때 가장 중요한 점은, client는 ACK bit을 보낸 후 2 * MSL(max segment lifetime)만큼의 시간을 대기한 후 CLOSED한다.
- ACK를 받은 server은 바로 CLOSED 상태에 진입한다.
- APP 레벨에선 종료했지만, TCP 레벨에선 종료하지 않았기에 APP을 재시동해도 몇 분 동안은 동작하지 않을 수 있다.
'Computer Network > Ch3) Transport layer' 카테고리의 다른 글
Ch3-4) Principles of reliable data transfer (0) | 2024.10.27 |
---|---|
Ch3-3) Connectionless Transport: UDP (0) | 2024.10.27 |
Ch3-2) Multiplexing and Demultiplexing (0) | 2024.10.27 |
CH3-1) Transport Layer Service (0) | 2024.10.27 |