TCP 데이터 패킷을 처리하며 필요한 타이머들과 옵션들을 알아보자.
- Retransmission: 데이터 패킷을 전송하고 Timer를 set한다. ACK이 소멸되면 Time-out이 발생한다.
- Persistence: rwnd가 0일 때, 데이터 송신이 중지되고, 이후 데이터를 받을 수 있을 때, 이에 대한 ACK을 보내는데, 이게 소멸됐을 때를 위해 영속 타이머가 필요하다. probe 패킷 전송
- Keepalive: 서버와 클라이언트가 데이터를 주고받다가, 클라이언트의 하드웨어 문제와 같은 문제로 다운됐을 때, 서버는 버퍼를 가지고 2시간동안 기다린다. 이후, 서버가 2시간동안 패킷이 돌아오지 않았을 때, probe 패킷을 전송하여 연결이 살아있는지 확인한다.
HTTP 통신 때는 필요없다. 웹 사이트 파일을 전부 받고 TCP 통신이 종료된다.- TIME-WAIT: close 과정에, 네트워크 연결이 종료되기 이전에, 2MSL만큼 기다린다.
이는 FIN에 대한 ACK 패킷이 소멸됐거나, 중복 포트넘버 할당을 방지하기 위함이다.
Retransmission timer의 시간을 RTT로 정하는데, 이는 가장 최근이 RTT가 아닌 누적된 값을 사용하여 정한다. 이를 어떻게 정하는지 알아보자.
- RTO = RTTs + 4*RTTd
RTO 초기값은 6ms이다.- RTTs = (1-a)RTTs + aRTTm
RTTs는 RTT의 평균이다. 이는 weighted되어 계산된다.(Exponential Weighted Moving Average)
RTTs 초기값 RTTm이다. RTTm은 가장 최근에 계산된 RTT이다.
가중치 a는 1/8로 계산된다. 이후 RTTm을 통해 위의 수식을 계산한다.- RTTd = (1-B)RTTd + B|RTTs-RTTm|
RTTd의 초기값은 RTTm/2이다.
이후 RTTm의 편차의 절댓값과 누적값의 평균을 구한다. 가중치 b는 1/4이다.
RTO는 SYN+ACK패킷이 올 때부터 계산된다.
- a가 너무 크다면 바로바로 최근값이 반영되겠지만, 평균값이 확 뛰어버린다.
위의 이미지에서 보듯이 RTT 계산은 ACK을 받은 이후, 처음의 것만 계산한다. 이 데이터 패킷에 대한 ACK이 오기 전에 보낸 패킷들은 계산하지 않는다. Receiver가 한 번에 다 처리하기 때문이다.
Time-out이 발생한 패킷은 처음 보낸 시점을 기준으로 RTT를 계산해야할까? 아니면 재전송을 기준으로 계산해야할까?
- (a): ACK 패킷이 소멸되어 Time-out이 발생해 데이터 패킷이 재전송된 시점이다.
- (b): Time-out이 발생하여 데이터 패킷이 재전송 됐지만, 네트워크 혼잡 때문에 늦게 도착한 경우다.
(b)와 같은 경우는 재전송 데이터 패킷 기준으로, ACK이 너무 빠르게 도착했기 때문에 RTT 측정값이 누적값과 크게 달라질 것이다. 이러한 이유로, RTTs와 RTTd를 계산하지 않는다.
RTT 측정은 재전송되지 않은 데이터 패킷에 대한 ACK이 들어온 이후에 재개한다.
Time-out이 발생하여 재전송한 데이터 패킷은 RTO 값을 두배로하여 전송한다.
이후 ACK 패킷을 받으면 이는 RTO값을 계산을 하지 않는다.
직접 측정된 RTT값을 확인할 수 있다. 보라색 선은 RTTs이다. 당연하게 RTTm에 비례한다.
EOP(End Of oPtion)은 1byte padding으로 4로 나누어주기 위함이다. 하나만 붙을 때 사용한다.
NOP(No OPeration)은 4의 배수를 맞출 기 위해 padding이 2 byte 이상 필요할 때 사용된다.
이렇게 하나 이상이 필요할 때 사용하며 옵션 앞쪽에 붙는다.(앞을 띄어야 하는 옵션들이 존재한다.)
default: 516bytes이다.
1k, 2k bytes까지 키우기 위해 MMS를 키우는 option을 사용할 수 있다.
이는 3 handshakes 과정에서 설정한다. 이는 receiver가 client에게 요청한다.
rwnd도 option으로 변경할 수 있다.
최대는 1 bytes만큼의 크기인 약 256이다. 이를 2^n의 n에 대입하면 기본 window size인 약 2^16에 2^256을 곱한 값이 된다. 최대값은 2^16 x 2^256이 된다.
기본적으로 window size의 최대인 2^16은 2^6 x 2^10 으로 나뉘어지며 이는 64kbytes이다
이는 512kbits며 RTT가 1초라면, 0.5Mbps의 쓰루풋을 가진다. 이게 최대값이다. cwnd가 높아져도 rwnd에 막힌다.
(얘는 당연히 키워야한다. 요즘은 Gbps다)
얘는 데이터 패킷에 출발 시간을 달고간다. 만약에 Sequence number를 16bit로 표현하는데, 이게 한 바퀴 다 돌아서 같은 Sequence number가 겹칠 경우 이를 보고 RTT를 계산할 수 있게된다. 패킷 구별이 가능하다는거다. 중복된 Seq number를 처리할 수 있다는 것이다.
당연히 Receiver는 이 시간을 적어서 ACK 번호를 보내준다.
그리고, RTT를 구하기 굉장히 쉬워진다. (현재시간 - Time Stamp)
만약 퐁당퐁당 데이터패킷이 없어지면 어떡할까? Receiver 입장에서, 맨 처음에 없어진 패킷에 대한 ACK을 보낼 것이다. 3 중복 ACK 또는 Time-out이 되기 이전까지 또 없어진 패킷을 알 수가 없다. 소멸된 패킷을 다 알아차리는데 오래걸린다는 것이다.
그래서 없어진 패킷에 대한 정보를 Sender에게 보낸다. *이를 보내지 않으면 소멸된 이후의 모든 패킷을 재전송하기 때문에 겹치는 패킷이 많아진다.
맨 처음의 option은 setup 때, SACK을 사용하겠다는 의미이다.
두 번째 option은 이후 데이터를 보내며 기록한다.
4001~6000, 8001~9000은 받았다는 정보이기 때문에, 재전송을 안한다 sender가.
Time-Stamp를 제외하고 싹다 Setup 과정에서 설정된다.