지난 시간 UDP를 relable한 전송을 위해 설계를 했는데요. 이번에는 TCP를 직접 다뤄보는 시간을 가졌습니다.
1. TCP
TCP/IP는 Vincent Cert, Robert Kahn이 만들었습니다. 끝없는 공방 끝에 현재 미국 주도의 표준이 되었죠. 처음에는 TCP/IP가 하나의 프로토콜이었습니다. 즉, 한 세트로 설계된 프로토콜이었습니다. 하지만, 기능이 방대해지다보니 분해해서 부르고 있습니다. TCP 프로토콜은 트랜스포트레이어 프로토콜이고, IP는 네트워크 레이어 프로토콜이 되었습니다.
1-1. IEEE Transactions on Communications Technology [1974]
엄청 역사가 길고, 체계적으로 유명한 저널입니다. 이 저널에 처음으로 TCP/IP 논문이 소개되었습니다.
1-2. ACM Turing Award in 2004
컴퓨터 사이언스 계의 노벨상이라고 부를 수 있다.
1-3. OVERVIEW
- rdt를 설계하며, 추가되었던 기능들이 (error detection, retransmissions, cumulative acknowledgements, seq# and ack#, timers) TCP에 녹아있습니다.
- TCP는 connection-oriented 라고 볼 수 있습니다. (TCP 커넥션 등)
- full duplex data : 커넥션이 완료되면 양방향으로 정보 전달이 가능하게 해주는 프로토콜 입니다.
- flow control, congestion control : TCP에서 중점적으로 봐야할 중요한 기능입니다. GO-BACK-N에서 N을 어떻게 제어하면 좋을까? 에 관련된 기능입니다. 즉, N 사이즈를 제어하기 위한 2가지 기능입니다.
- MSS: Maximum Segment Size : 밑에 링크 레이어에서 받아줄 수 있는 최대 사이즈를 정의합니다.
Q. TCP 센드 버퍼로 데이터 write할 때 우리가 사용하고 있는 인터페이스는 무엇일까요?
소켓입니다. 소켓을 통해서 어플리케이션 레이어에서 TCP 레이어로 데이터를 쓰게 됩니다.
TCP send buffer에서는 데이터를 링크를 통해서 보내주고, TCP receiver buffer에선 데이터를 받아서 어플리케이션 레이어로 올려주게 됩니다.
1-4. TCP segment structure
UDP보다 많은 정보를 요구하는 것을 볼 수 있습니다. TCP는 길이가 길다보니 오버헤드가 많습니다.
대표적으로 소스포트/데스티네이션 포드가 들어있습니다. UDP와 같죠.
시퀀스넘버가 필드에 들어있습니다. (32비트 차지)
액넘버도 사용됩니다(32비트)
리시브 윈도우 : 플로우 컨트롤에서 배웁니다.
옵션필드 : 미래에 추가될 정보가 있으니 남겨두는 필드입니다. TCP는 갈아엎기 힘들기 때문에 추가 옵션을 통해 많은 기능을 제공하고자 한다.
U(urgent): 긴급한 패킷 표시하기 위한 용도입니다. (1 or 0) 실제로는 잘 안씁니다.
A(acknowledgement):이 패킷이 acknowledgement을 위한 것인지 데이터인지를 구분하기 위한 식별자입니다.
P(push data)
R,S,F : 연결에 필요한 필드, 연결을 맺고 끊는 부분 설명할 때 알 수 있을 예정
1-5. TCP에서 시퀀스넘버와 ACKs
시퀀스 넘버를 붙일 때 각 TCP 세그먼트(바이트 넘버)의 1번째 바이트 넘버가 시퀀스 넘버가 됩니다. 1000바이트 단위의 MSS가 있다고 하면 1번째 세그먼트는 0이 시퀀스 넘버이고, 2번째 세그먼트는 1000이 시퀀스 넘버입니다.
ACK넘버는 첫번째 세그먼트를 받았을 때 그 다음의 시퀀스 번호를 ACK (ex 1000, 2000)으로 보냅니다.
이를 보아 TCP는 cummlative ack 방식이라고 생각할 수 있습니다. (내가 2000을 받은 이유는 1999까지 잘 받았기 때문이야)
TCP ack일 경우 A에 1이 켜지는 겁니다.
TCP 헤더를 네트워크 레이어의 IP헤더가 감싸는 형태입니다. 참고로 TCP+IP 헤더는 총 40바이트의 오버헤드가 붙습니다.
1-6. ACK
TCP 리시버가 ACK을 사용합니다. 왜냐하면 데이터를 잘 받았다고 알려주기 위한 용도입니다. ACK패킷의 낭비를 최소화하기 위해 ACK에 데이터를 실어서 보내자라는 시스템이 나오게 됩니다. 이런 것을 ACK이 데이터에 piggybacked 되었다 라고 합니다.
TCP에서는 데이터에 ACK을 보내는 경우도 생기고, 데이터가 없을 경우 ACK만 보내는 경우도 생깁니다.
누적 ACK 방식의 좋은점은 이전의 데이터가 loss가 났을 때도 다음 번 ACK 패킷이 잘 갔다면, 손실이 없습니다. 예를 들어 5번 로스이고 10번 ACK을 잘 받았다면 이 의미는 10번 패킷까지 잘 받았다는 의미가 되므로 손실이 없는겁니다.
문제를 풀어봅시다 ^_^ 1byte짜리 데이터를 실어서 보내주고 있다고 가정합시다. 그럼 아래와 같습니다.
?는 45와 82가 나옵니다. 시퀀스 넘버 42로 데이터를 보냈고, 첫번째 액 넘버는 79를 알려주고 있고, 데이터는 3바이트 짜리입니다.
42/43/44로 3바이트가 구성되어 있을 겁니다. 그럼 그 다음 시퀀스 넘버는 45로 시작되겠죠.
액은 항상 그다음 받을 넘버이기 때문에 45인 겁니다. 또한 액넘버 79로 받았으니 시퀀스는 79가 됩니다.
다음 액은 시퀀스 넘버 79에 실려 들어온 데이터이므로 79/80/81에 있는 HI 데이터를 받고 82 액넘버를 갖는겁니다.
2. TCP : timeout에 대하여
타임아웃을 너무 짧게 잡으면, 불필요한 재전송이 생기고, 길게잡으면 재전송을 판단하기까지 너무 오랜시간을 기다려야 합니다.
2-1. Estimated RTT
1) moving average 방식
현재 RTT가 아닌 대략적으로 스무딩 된 RTT를 구하는 것입니다.
n번째 시점에서의 estimated RTT는 EstimatedRTT(n) = (1- a)*EstimatedRTT(n-1) + a*SampleRTT(n)로 구합니다.
과거의 값을 (1-a)만큼 현재값을 a만큼 반영하겠다는 의미입니다. (과거값 누적해 반영하면 스무딩이되는 효과가 있습니다.)
이런 방식을 좀 더 어려운 말로 exponential weighted moving average (EWMA)이라고 합니다.
알파를 크게 잡는다면 현재가 많이 반영되는 셈입니다. sample RTT가 굉장히 폭이 큰 것을 볼 수 있는데요. 들쭉날쭉한 RTT를 기준점으로 삼는다면 타임아웃 값이 너무 왔다리갔다리할 겁니다. 하지만, Estimated RTT를 사용하면 어느정도 기준점이 잡히는 것을 볼 수 있습니다. 때문에 알파를 0.125으로 작게 잡는 것입니다.
2) RTT deviation
그런데 이런 스무딩값을 쓰고, 현재와 많이 차이가 난다면 좀 불안하겠죠. (동그라미 부분) 그래서 안전장치를 추가합니다.
변동성에 대한 마진을 더해주면 됩니다.(=Dev RTT,분산) 근데 마진이 고정이 아니고 변동성이 큰 구간의 마진을 크게 잡아야 하고, 변동성이 비슷하면 마진을 작게 잡아야 합니다.
N번째 DevRTT는 N-1번째 Dev RTT를 1-a만큼 반영하고, 현재 시점에서 측정한 sample rtt와 현재 시점에서 계산된 estimated rtt의 차이를 a만큼 반영해주면 됩니다.
3) 최종 타임아웃 값
최종적으로 타임아웃 값은 TimeoutInterval = EstimatedRTT + 4*DevRTT으로 구할 수 있습니다.
안전 마진으로 현재 시점에서 측정한 sample rtt와 현재 시점에서 계산된 estimated rtt의 차이를 계산한 것에 4배를 더해줍니다.
3. TCP : reliable한 데이터 전송에 관하여
신뢰성있는 데이터 전송을 위해 TCP에서는 single retransmission timer를 사용하고 있습니다. 실제 ack을 못받은 패킷에 대해 타이머를 적용하는 게 필요합니다. 문제는 각각 ack을 못받은 애만 타이머를 돌리게 되면 complexty가 너무 높아집니다. 모든 타임아웃값을 관리하는 것은 불가능한 것이죠. 때문에 single retransmission timer를 사용하고 있습니다.
3-1. duplicate ack이 없고, flow control&congestion control이 없는 상황
세그먼트를 생성할 때 시퀀스 번호를 붙여줍니다.
세그먼트에서 첫번째 바이트가 시퀀스 넘버가 됩니다.
현재 타이머가 돌고있지 않을 때만 타이머가 돌아갑니다. (=싱글타이머)
액을 못받은 세그먼트들이 여러개 있다면 액이 안들어온 세그먼트들 중 가장 오래된 애를 기준으로 타이머가 돌아갑니다.
타임아웃이 만약 터졌다면, 가장 오래된 애 기준이기 때문에 가장 오래된 애의 패킷을 재전송 해줍니다.
그 후 다시 타이머를 리셋해줍니다. 그리고 unacked segments를 acked로 바꿔줘야 합니다.
위 상황에서도 ?는 120인것 같네요. cummlative!
타임아웃 값은 EstimatedRTT(n) = (1- a)*EstimatedRTT(n-1) + a*SampleRTT(n),
TimeoutInterval = EstimatedRTT + 4*DevRTT 로 구할 수 있고,
타임아웃이 연속적으로 터질 수 있는데요. 이 의미는 타임아웃값이 짧다는 것을 의미할 수도 있습니다. 네트워크 상황 혼잡/상황 반영을 못하고 있어 불필요한 재전송이 많아짐을 뜻할 수도 있습니다. 혼잡가중!
때문에 재전송이 연속해서 일어나면, 타임아웃 값을 2배씩 늘려줍니다.
E.g., current timeout = 0.75s, another timeout occurs, then next timeout = 0.75 x 2 = 1.5s, next timeout = 1.5 x 2 = 3.0s, ...
네트워크 속도를 빠르게함과 동시에 congestion을 줄이자 상충된 목표를 달성하고자 하는 게 TCP의 목표입니다.
3-2. TCP ack : Delayed ACK
Delayed ACK : TCP에서 액을 보내는 것을 바로 보내지 않고 좀 딜레이 시켜서 보내자라는 것입니다.
왜 액을 늦게보내자고 했냐면, 가능하면 액만 날라가는 패킷을 줄이고 싶기 때문입니다. 좀 더 기다리면 운좋게 데이터 발생하는 시점과 맞을 수도 있기 때문이에요. 대신 안좋은 점이 있는데요. 피드백받는 시간을 늦추는 방법이기 때문에 타임아웃을 자주 부르게 될 수 있습니다. 타임아웃이 터지면 재전송이 터지는 데 더 안좋은 방법이 될 수 있단거죠.
약 500ms 까지 지연시켜보내자는게 디폴트로 잡혀있어요. (약 1초의 반) 최대한 타임아웃을 줄이자!
이 방식의 기대점은 기다리는 딜레이 내에서 리시버가 보낼 데이터가 생길 수 있는데 데이터 보낼 때 액실어 보내면 자원을 아낄 수 있지 않을까입니다.
Delayed ACK을 잘 활용하고자 exception을 달아놨는데요. 최대 두개의 세그먼트를 받을 때까지 딜레이되면 안된다라는 것입니다.
또한 네트워크가 혼잡해 타임아웃이 많이 일어난다면, 리시버 단에서는 네트워크가 혼잡하지 않다면, 패킷이 순서대로 들어올 확률이 높다고 봅니다. 그게 아니면 패킷이 뒤죽박죽 들어오는데요. 그럴 땐 Delayed ACK기능을 off시키도록 해두었습니다.
'CS > 컴퓨터 네트워크' 카테고리의 다른 글
컴퓨터 네트워크 16일차 : TCP congestion control (0) | 2021.10.31 |
---|---|
컴퓨터 네트워크 14일차 : fast retransmit / flow control / connection management (0) | 2021.10.18 |
컴퓨터 네트워크 12일차 : 소켓프로그래밍 멀티쓰레드 (0) | 2021.10.12 |
컴퓨터 네트워크 11-2일차 : 소켓프로그래밍 (0) | 2021.10.06 |
컴퓨터 네트워크 11-1일차 : Pipelined protocol (GO-BACK-N / Selective Repeat) (0) | 2021.10.06 |