TCP: sequence number, timeout, fast retransmit

Tasker_Jang·2026년 9월 24일
post-thumbnail

1. TCP의 네 가지 성격

  • point-to-point: 송신자 하나와 수신자 하나. UDP처럼 여러 상대에게 뿌리는 구조가 아닙니다.
  • full duplex(전이중): 한 연결에서 양방향으로 동시에 데이터가 흐릅니다. 그래서 양쪽 모두 자기 sequence number를 따로 관리합니다.
  • connection-oriented: 데이터를 보내기 전에 양쪽이 상태를 맞춥니다. 연결 설정과 종료 절차는 뒤에서 다룹니다.
  • flow control(흐름 제어)과 congestion control(혼잡 제어)을 함께 수행합니다.

2. sequence number와 acknowledgment number

TCP는 패킷이 아니라 byte stream을 세는 점이 앞서 본 rdt와 다릅니다.

필드값의 의미
sequence number이 세그먼트의 첫 바이트가 전체 스트림에서 몇 번째인가
acknowledgment number상대에게서 다음에 받기를 기대하는 바이트 번호

예를 들어 sequence number 1000인 세그먼트에 500바이트가 실려 있으면, 수신 측은 acknowledgment number 1500을 돌려줍니다. "1499번까지 다 받았고 다음은 1500번"이라는 뜻입니다. cumulative ACK가 바이트 단위로 구현된 형태입니다.

full duplex이므로 ACK는 별도 세그먼트가 아니어도 됩니다. 보낼 데이터가 있으면 그 세그먼트 헤더의 acknowledgment number에 얹어 보냅니다.

3. RTT와 timeout의 관계

타이머 값은 크게 잡아도 작게 잡아도 손해입니다. 짧으면 멀쩡한 세그먼트를 다시 보내고, 길면 진짜 손실에서 복구가 느려집니다. 기준은 RTT여야 하는데, RTT는 경로와 혼잡도에 따라 계속 변합니다.

그래서 TCP는 측정한 값 하나를 그대로 쓰지 않고 EstimatedRTT(추정 RTT)를 유지합니다. 새 측정값이 들어올 때마다 기존 추정치를 조금씩 갱신하는 지수 가중 이동평균입니다.

ALPHA, BETA = 0.125, 0.25

def update(estimated_rtt, dev_rtt, sample_rtt):
    dev_rtt = (1 - BETA) * dev_rtt + BETA * abs(sample_rtt - estimated_rtt)
    estimated_rtt = (1 - ALPHA) * estimated_rtt + ALPHA * sample_rtt
    timeout = estimated_rtt + 4 * dev_rtt
    return estimated_rtt, dev_rtt, timeout

α가 0.125이므로 새 측정값은 8분의 1만 반영됩니다. 한 번 튄 값에 타이머가 요동치지 않게 하려는 것입니다.

그런데 평균만으로는 부족합니다. RTT가 들쭉날쭉한 경로라면 평균 근처에 타이머를 두면 자주 헛발질합니다. 그래서 변동폭 DevRTT를 따로 추정해 안전 마진으로 더합니다. 최종 식은 EstimatedRTT + 4 × DevRTT입니다. RFC 6298은 α를 1/8, β를 1/4로 쓰도록 하고, 측정값이 하나도 없는 초기에는 타이머를 1초로 두도록 권합니다.

두 가지 예외가 더 있습니다. timeout이 발생하면 다음 타이머 값을 이전의 두 배로 늘립니다. 또 재전송한 세그먼트에 대해서는 RTT를 측정하지 않습니다. 돌아온 ACK가 원본에 대한 것인지 재전송분에 대한 것인지 알 수 없어 값이 오염되기 때문입니다.

4. single timer와 재전송 시나리오

selective repeat은 세그먼트마다 타이머를 하나씩 두었지만, TCP 구현은 타이머를 하나만 씁니다. 가장 오래된 미확인 세그먼트에 대해서만 재는 것입니다. 타이머 수십 개를 관리하는 비용을 피하는 현실적 타협입니다.

두 번째 시나리오가 cumulative ACK의 이점입니다. ACK 하나가 사라져도 뒤따르는 ACK가 앞을 포함하므로 재전송이 일어나지 않습니다.

5. fast retransmit

timeout을 기다리는 것은 느립니다. 타이머는 안전 마진까지 더해 잡혀 있으니, 손실 하나 때문에 수백 밀리초를 그냥 흘려보내게 됩니다.

단서는 duplicate ACK(중복 ACK)입니다. 수신 측은 중간이 비면 계속 같은 acknowledgment number를 돌려보냅니다. 송신 측이 같은 ACK를 반복해서 받는다는 것은, 뒤의 세그먼트들은 도착하고 있는데 중간 하나가 비었다는 신호입니다.

기준은 중복 ACK 3개입니다. 왜 하필 셋인가 하면, 단순히 순서가 뒤바뀌어 도착한 경우에도 중복 ACK가 한두 개는 생기기 때문입니다. 세 개 이상 연달아 오면 재정렬이 아니라 손실이라고 보는 것이 타당하다는 경험칙입니다. 이때 타이머 만료를 기다리지 않고 바로 재전송하는 것이 fast retransmit(빠른 재전송)입니다.

6. flow control과 congestion control

이름이 비슷해 헷갈리지만 막으려는 대상이 다릅니다.

flow controlcongestion control
누가 제한하나수신 측이 window 필드로 알림송신 측이 스스로 추정해 조절
무엇을 보호수신 측 버퍼네트워크 내부의 라우터 큐
신호상대가 알려주는 남은 버퍼 크기손실과 중복 ACK
위험받는 쪽이 못 따라가 버퍼 넘침코어가 붐벼 패킷이 버려짐

flow control은 상대가 감당할 수 있는 만큼만 보내는 장치입니다. 수신 측은 자기 버퍼의 남은 공간을 헤더의 window 필드에 담아 매번 알려주고, 송신 측은 미확인 데이터가 그 값을 넘지 않게 유지합니다. 반면 congestion control은 아무도 알려주지 않는 상태를 스스로 짐작해야 하는 문제입니다.

profile
ML Engineer 🧠 | AI 모델 개발과 최적화 경험을 기록하며 성장하는 개발자 🚀 The light that burns twice as bright burns half as long ✨

0개의 댓글