본 내용은 한양대학교 이석복 교수님의 강의를 참고하여 정리하였습니다. 교재는 Pearson/Addison Wesley에서 출판한 Computer networking : a top-down approach입니다.
TCP 개요
- Point to Point : TCP 연결에서 양쪽의 TCP가 한쌍을 이룬다.
- Reliable , in-order byte stream : 신뢰성과 순서를 유지하는 전송
- Pipelined : 메시지를 한 번에 여러 개 보내고 받을 수 있는 통신. 하나 보내고 응답 받고 다른 거 보내는 방식이 아니다.
- Full-duplex data : TCP 연결에서 양쪽의 TCP는 receiver와 sender의 역할을 모두 수행
- Connection-oriented
- Flow controlled : 보내는 쪽은 받는 쪽의 속도에 맞춰서 데이터를 전송
TCP segment structure
1) Sequence number
- 한 segment의 sequence number는 맨 앞 byte의 번호
2) Acknowledgement number (ACK)
- 피드백 번호
- ACK number의 의미
* ACK #100의 의미는 '99번까지 안정적으로 받았으니 100번을 기다린다'.
3) Timer
- (Timer는 Header에 없지만, 마땅히 놓은 카테고리가 없어서 여기에 놓는다)
- 보내는 쪽에서 segment를 보내면 Timer가 시작하고, 순차적으로 다음 번호를 달라는 피드백이 오면 timer 초기화
- Timer는 RTT(Round Trip Time)로 설정
* Timer = RTT + margin
- RTT를 구하는 방법
* RTT = (1-α) * RTT + α * sample RTT, typically α = 0.125
* RTT를 구하는 방법은 weighted moving average를 이용한다.
* Sample RTT는 보내는 쪽에서 segment를 보내면 매번 측정한다. 그리고 RTT를 계속 갱신한다.
* Sample RTT는 매번 큰 폭으로 변화가 일어나고 있다. 이렇게 변화가 일어나는 이유는 네트워크 환경이 다이나믹하게 변하기 때문이다. 구체적으로 이야기 하자면, segment가 보내지는 과정에 queueing delay이가 다르기 때문이다.
* Segment 재전송이 일어날 때는 그 때의 sample RTT는 계산에 포함시키지 않는다. 왜냐하면, sample RTT를 정확하게 측정할 수 없기 때문이다.
TCP reliable data transfer
- 구조
* TCP 연결이 생성되면 socket과 연결된 TCP에 2개의 buffer가 생성된다.
* Send buffer
+ Application layer에서 보내진 데이터가 socket을 통해 TCP로 보내지는 속도와 TCP에서 전송하는 속도의 차이가 있으므로, 이를 중간에서 조율하기 위해서 buffer가 존재
+ 데이터 유실을 방지하기 위해서 데이터를 가지고 있다가 재전송 (아래에서 이해 가능)
* Receiver buffer
+ 받은 데이터의 순서가 맞을 때까지 데이터를 기다렸다가 socket을 통해서 application layer로 보냄 (아래에서 이해 가능)
- 동작
* TCP는 segment를 보낼 때, window size 만큼 보낼 수 있다.
+ window size가 1000byte이고 segment 크기가 200byte면, 5개의 segment를 한 번에 보낼 수 있다.
* 초기 : window size만큼 보내기 전
+ TCP 연결, buffer 2개 생성
* 진행 1
+ Window size 만큼의 segment들이 보내진다.
+ Timer는 #0을 기준으로 시작한다.
* 진행 2
+ 이해를 하기 위해서 우선 #0만 들어왔다고 하자
+ Receiver에서 #0을 받으면 ACK#200을 보낸다.
+ #0 segment는 socket을 통해 application layer로 보낸다.
* 진행 3
+ ACK#200을 받았으니 window를 #200으로 옮긴다.(slide)
+ Window를 옮겼으니 #1000을 보낸다.
+ Timer도 #200으로 옮기고 초기화 한다.
* 진행 4
+ 이해를 하기 위해서 우선 #200만 들어왔다고 하자
+ Receiver에서 #200을 받으면 ACK#400을 보낸다.
+ #200 segment는 socket을 통해 application layer로 보낸다.
* 진행 5
+ ACK#400을 받았으니 window를 #400으로 옮긴다.(slide)
+ Window를 옮겼으니 #1200을 보낸다.
+ Timer도 #400으로 옮기고 초기화 한다.
* 진행 6
+ 이제 모든 segment가 우루루 들어온다고 하자. 그런데 #400이 중간에 유실되었다.
+ Receiver에서 #600을 받았지만 ACK#400을 보낸다. 즉, #400 순서인 segment가 필요하다는 의미 전송
+ Receiver에서 #800을 받았지만 ACK#400을 보낸다. 즉, #400 순서인 segment가 필요하다는 의미 전송
+ Receiver에서 #10000을 받았지만 ACK#400을 보낸다. 즉, #400 순서인 segment가 필요하다는 의미 전송
+ Receiver에서 #1200을 받았지만 ACK#400을 보낸다. 즉, #400 순서인 segment가 필요하다는 의미 전송
+ #600, #800, #1000, #1200은 #400이 오지 않아서 application layer로 보내지지 않고 buffer에 남아 있는다.
+ 위 관찰을 통해, receiver buffer의 역할은 데이터들의 순서를 유지시켜주는 것을 알 수 있다.
* 진행 7
+Timer는 #400 위치에서 계속 작동중이다. ACK#400이외에 다른 ACK가 오지 않는다.
+ Timer가 만료되고 #400 segment를 재전송한다.
+ 위 관찰을 통해, send buffer의 역할은 데이터가 손실되었을 때 재전송을 통해 데이터 손실을 방지한다.
* 진행 8
+ Receiver에서 #400을 받고 ACK#1400을 보낸다. 가장 마지막 segment을 기준으로 한다.
+ 전체 segment를 socket을 통해 application layer로 보낸다.
* 진행 9
+ ACK#1400을 받았으니 window를 #1400으로 옮긴다.(slide)
+ 이하 과정을 동일하다.
- 재전송 예시
- 재전송 방법 개선 : TCP fast retransmit
* 진행 7에서 재전송되는 방법은 Timer가 만료되었을 때였다. 그런데 더 빨리 재전송을 보낼 수 없을까?
* 진행 6을 보면 ACK#400이 연속해서 보내지는 것을 볼 수 있다. 이를 이용해서 feedback을 받는 쪽에서 동일한 ACK number를 4번 받으면 재전송 보내게 하는 방법으로 개선할 수 있다.
* 4번이라는 선택의 이유는 engineering decision의 결과이다.
과제
Draw a detailed packet-exchange diagram(e.g., seq#, ack#) until the reception of complete file
- Assume that TCP connection has been stablished between A and B
- Host A will transmit 600-byte file
- The seq# of the first data packet (from A) : 300
- All data packets are 100 bytes
- Window size : 1000
- Retrasmission timeout = 500ms, RTT = 50ms
- Second data packet is lost
- Host A uses fast retransmit.
Diagram
Flow control
- TCP 송신자가 데이터를 너무 빠르게 또는 너무 많이 전송하여 수신버퍼가 overflow하는 것을 방지한다
- 동작
* Receiver buffer에 빈 공간(=RcvWindow)이 얼마 만큼 있는지 알린다.
* Spare room in buffer = RcvWindow = RcvBuffer - (LastByteRcvd - LastByteRead)
+ 예시 : 1000 - (700 - 100) = 400 byte
* RcvWindow 값을 구해서 Header에 넣어 sender에게 보낸다. 아래 header의 Receive window에 값을 넣는다.
* Sender의 window size는 receive window의 값에 맞추어 변한다.
* window size가 작아지면 보내는 데이터의 양이 줄어들고, 커지면 보내는 데이터의 양이 증가한다.
- 문제
* Receive window 값이 0이면 sender는 보내는 값이 없다. 이후, receiver buffer에 공간이 생기게 된다면 어떻게 sender에게 이 사실을 알릴 수 있을까?
* 왜 알릴 수 없을까?
+ Segment를 보내는 첫 번째 경우 : 받은 segment에 대해 feedback segment를 보낸다.
+ Segment를 보내는 두 번째 경우 : 해당 TCP가 보낼 데이터가 있어서 segment를 보낸다.
+ 위 상황은 receiver가 sender에게 데이터를 보내지 말라는 상황이다. 즉, 두 번째 경우에서 보낼 것이 없어진 상황이다. 따라서 receiver는 sender에게 연락할 방법이 없어서 dead lock이 된다.
* 방법
+ Receive window = 0을 받은 sender는 receiver에게 주기적으로 아주 작은 segment를 보낸다. 아주 작은 segment는 데이터가 1 byte가 들어간 segment가 예시가 될 수 있다. 이 segment를 받으면 receiver는 드디어 sender에게 segment를 보낼 수 있다.
Connection management
- TCP connection의 연결과 해제
1) 연결
- 3-way handshake
* 데이터를 교환하기 전에 연결하는 방법
* SYNbit와 ACKbit는 flag로 연결 동의 파라미터이다. Segment structure의 S와 A이다.
* 첫 번째 전송과 두 번째 전송에서는 header만 보낸다. 세 번째 전송에서는 데이터를 포함할 수 있다.
- 2 way handshake
* 2 way handshake는 하지 않는다. 왜냐하면 Sequence number를 synchorize할 수 없기 때문이다.
* 내 생각 : client에서 sequence number와 SYNbit를 보내고, server에서 반대로 sequence number와 SYNbit를 보내면 된다고 생각했다. 마치, 내가 A에게 "나 동맹 맺을래. 여기 내 정보"라고 말하고, A는 나에게 "그래, 나도 동맹 맺을래. 여기 내 정보"라고 말하는 것 처럼 말이다.
* 내 생각의 문제점과 2 way handshake가 안되는 이유 : 내 생각에서 A가 동맹 맺자고 말했을 때, 동맹을 증명할 증거가 필요하다. 즉, 고개를 끄덕이던가 "좋아 우리 동맹이다"와 같은 말이 필요하다. 그래서 sever가 sequence number와 SYNbit를 보내면 server는 client가 연결을 실질적으로 승인했는지 확인이 필요하다. 그래서 다시 ACK를 받야 하기 때문에 2 way로는 연결하기 힘들다.
2) 해제
- closing connection
* 서로 연결을 끝자는 메시지를 보내고 ACK를 받으면 연결 해제
* Client가 마지막으로 ACK를 보내고 바로 연결을 끊지 않는 이유?
+ TIMED_WAIT 이후에 2 * max segment lifetime 동안 기다린다. 그 이유는 다음과 같다. Client에서 ACK를 보내고 연결을 끝나고 가정해보자. 그리고 ACK는 도중에 유실된다고 가정해보자. 이럴 경우, server에서 걸어놓은 timeout이 만료되면서 server에서 메시지를 재전송한다. 그런데 client는 없다. 다시 timer가 만료된다. server는 메시지를 재전송한다. 이런 현상이 발생하기 때문에 의도적으로 client를 일정시간 동안 연결시켜 놓고 나중에 종료시킨다.
Congestion control
- 네트워크 상황을 고려하여 데이터 전송량(window size) 조절
- Congestion : 네트워크가 처리할 수 있는 양보다 데이터가 많이 들어오면 발생하는 현상으로 손실과 queueing delay가 발생
Congestion을 이해하기 위한 시나리오
- 가장 이상적인 시나리오에서 시작해서 실제 시나리오와 비슷한 상황으로 전개
1) 시나리오 1 : 2개의 송신자와 1개의 라우터, 라우터의 무한 버퍼
- 동작
* 두 호스트가 최대 속도로 데이터를 전송해도 라우터의 버퍼는 무한이므로 언제가는 서버에 도착한다. 따라서 재전송은 없다.
- 성능
* 연결 전송률을 나타낸 그래프이다. 라우터의 bandwitdh이 R이면 각각 R/2 만큼의 양을 보낸다. R/2에 도달했을 때, 서버에서 처리할 수 있는 양은 R/2로 유지된다.
* 보내는 양이 R/2에 가까워 질 수록 라우터의 버퍼는 점점 차기 시작한다. 무한대의 크기이기 때문에 딜레이는 계속 증가한다.
2) 시나리오 2 : 2개의 송신자와 1개의 라우터, 라우터의 유한 버퍼
- 시나리오 2-1 : 호스트가 라우터의 버퍼에 얼마만큼의 공간이 남아 있는지 알고, 전송 가능할 때만 패킷 전송
* 시나리오 1과 동일하게 처리량은 λin으로 손실이 발생하지 않는다.
- 시나리오 2-2 : 호스트는 패킷이 유실된 것을 알고 재전송
* 패킷이 유실되어 처리량이 R/2에 바로 도달하지 못하지만, 재전송으로 이후에 처리량이 R/2가 된다.
- 시나리오 2-3 : 호스트는 패킷 유실을 모른다. Timer을 통해서 알 수 있다.
* Timer가 만료되어 재전송을 하게 된다. 재전송으로 인해 네트워크 상에 중복 패킷이 점차 증가하면서 지연이 발생한다. 또한 수신 측에서는 중복 패킷을 처리해야 한다. 따라서 처리량은 감소하게 된다.
3) 시나리오 3 : 여러 송신자와 라우터, 그리고 유한 버퍼
- 만일, 여러 호스트가 같은 라우터를 거쳐서 많은 데이터를 보내게 된다면 네트워크 지연과 데이터 유실이 발생할 것이다.
제어 방법
1) AIMD (Additive Increase Multiplicative Decrease)
- Additive Increase : 매 RTT마다 1MSS(max size segment)씩 cwnd(congestion window size)를 증가시킨다.
- Multiplicative Decrease : timer 만료 또는 3 duplicated ACK 발생 후에 cwnd를 절반으로 감소시킨다.
- 동작
* 송신자는 패킷을 보내고 ACK를 Timer 내에 받으면 1 MSS를 증가시킨다.
* 송신자가 패킷을 보내고 ACK를 받지 못하고 Timer가 만료되면, congestion이라고 판단하고 윈도우 크기(cwnd)를 절반으로 줄인다.
* 이때 윈도우의 크기는 rwnd와 cwnd 중에 더 작은 것으로 결정하고 데이터를 보낸다.
- 그래프 설명
* cwnd의 크기는 선형으로 증가하다가, 유실이 발생하면 크기가 반으로 줄어든다.
2) TCP slow start
- 시작할 때, 1 MSS 크기의 패킷을 보낸다. 이후에 2배씩 증가시킨다(지수적으로 증가). 그리고 threshold 값에 도착하면 멈추고 AIMD로 변경한다.
- TCP slow start 사용 이유 : AIMD를 바로 사용하기에는 부족한 점이 있다. 시작 cwnd를 얼마로 할지 모른다는 것이다. 자칫 크게 하면 초반부처 네트워크에 혼잡이 유발될 수 있다.
3) 유실 발견 시 대응
- 유실 판단 기준
* Timer
+ 송신된 패킷이 전송되지 못한 상황이므로 네트워크가 혼잡
* 3 duplicated ACK
+ 송신된 패킷이 전송되어 수신 쪽에서 피드백을 준 상황이다. 따라서 네트워크는 원활
- TCP Tahoe
* Timer 또는 duplication 발생 시에 TCP slow start 시작
* Threshold 값은 유실이 발생 했을 때의 cwnd의 절반
- TCP Reno
* Timer일 때, TCP slow start 시작
* Duplication일 때, cwnd를 절반으로 줄임
4) 흐름도
TCP 공평성
- 어느 TCP 차별 없이 동등한 송신률을 보장해야 한다.
- 예시 : 2개의 연결과 라우터의 bandwidth이 R이라면, 각각 R/2 송신률이 보장되어야 한다.
- 동작
* 각각의 전송률 합이 R보다 작으면 동일한 일정 전송률을 추가한다.
* 추가했을 때 총 전송률이 R보다 높으면 각각 반으로 감소시키니다.
* 위 과정을 반복한다.
- 예시
* 전송률 R(= 9)를 갖는 하나의 링크를 공유하는 호스트 A와 B가 있다. 각각의 전송률은 C1과 C2이다.
* 최초 C1 = 6, C2 = 2이다.
* C1 + C2의 합은 8이므로 아직 R보다 작으므로 1씩 더한다.
* 총합이 10이 되었으므로 반으로 감소시킨다. C1 = 3, C2 = 1.
* 위 과정 반복하면 결국에 C1 = 5, C = 5와 비슷하게 된다.
만일, 다른 기능들은 모두 유지되고 congestion control만 없다면 어떻게 될까?
- 결과 : 보내는 쪽은 보내고 받는 쪽은 받지 못함
- 이유 : window size 조절이 안 되므로 막 보내게 된다. 예를 들어, 모든 host가 엄청난 사이즈로 보낸다고 하자. 네트워크는 꽉꽉 막히게 될 것이다. 그러면 유실되고 재전송되고 중복 패킷이 많아질 것이다. 그래서 window size를 조절하는 congestion control이 필요하다.
참고
- 블로그 1
- 블로그 2
'Software Courses > Network' 카테고리의 다른 글
Network : overview (0) | 2021.01.06 |
---|---|
Transport : Segment의 크기 (0) | 2021.01.05 |
Transport : RDT (Reliable Data Transfer) (0) | 2021.01.02 |
Transport : UDP (0) | 2020.12.31 |
Transport : Multiplexing, Demultiplexing (0) | 2020.12.31 |