Skip to content
난이도 면접 빈출문제은행 3

TCP의 신뢰성과 혼잡 제어 ​

TCP(Transmission Control Protocol, 전송 제어 프로토콜)는 도착을 보장하지 않는 IP(Internet Protocol, 인터넷 프로토콜) 위에서 "빠짐없이, 순서대로"를 만들어 낸다. 방법은 데이터에 번호를 매기고, 받았다는 확인을 받고, 확인이 안 오면 다시 보내는 것이다. 여기에 속도 조절 두 가지가 더해진다. 받는 쪽이 넘치지 않게 하는 흐름 제어(flow control)와, 네트워크 중간이 막히지 않게 하는 혼잡 제어(congestion control)다.

순서 번호와 누적 ACK ​

TCP는 보낼 데이터를 적당한 크기로 잘라 앞에 제어 정보를 붙여 보내는데, 이 전송 단위를 세그먼트(segment)라고 한다. TCP는 연결에서 보내는 데이터를 바이트 단위로 센다. 각 세그먼트의 헤더(header)에는 그 세그먼트의 첫 바이트가 몇 번째인지를 뜻하는 순서 번호(sequence number)가 적힌다. 받는 쪽은 이 번호로 뒤바뀐 순서를 바로잡고, 같은 데이터가 두 번 오면 하나를 버린다.

받는 쪽은 ACK(Acknowledgement, 확인 응답)로 어디까지 받았는지 알린다. ACK 번호는 받은 마지막 바이트 번호가 아니라 다음에 받기를 기대하는 바이트 번호다. 1~1000번 바이트를 받았으면 ACK 번호는 1001이다. 이 번호 앞까지는 빠짐없이 받았다는 뜻이므로 누적 확인 응답(cumulative acknowledgment)이라고 한다. 누적 방식이라 중간 ACK 하나가 사라져도 뒤의 ACK가 앞 내용까지 함께 확인해 준다. 반대로 중간 데이터가 빠지면 그 뒤를 받았더라도 ACK 번호는 빠진 자리에 멈춰 있다.

재전송과 RTO ​

보내는 쪽은 세그먼트를 보내면서 타이머를 건다. 정해진 시간 안에 그 데이터를 확인하는 ACK가 오지 않으면 잃어버렸다고 보고 재전송(retransmission)한다. 이 제한 시간을 RTO(Retransmission Timeout, 재전송 타임아웃)라고 한다.

RTO는 고정값이 아니다. TCP는 세그먼트를 보내고 ACK가 돌아오기까지 걸린 RTT(Round-Trip Time, 왕복 시간)를 계속 재서, 그 평균과 흔들림 정도로 RTO를 정한다1. RTO가 너무 짧으면 멀쩡히 오는 중인 데이터를 괜히 다시 보내고, 너무 길면 진짜 손실에서 오래 멈춘다. RFC(Request for Comments, 인터넷 표준 문서) 6298은 RTO를 1초보다 작게 잡지 않기를 권하고, 타임아웃이 날 때마다 RTO를 두 배로 늘리게 한다. 네트워크가 막혀서 생긴 손실이라면 급하게 다시 보낼수록 더 막히기 때문이다.

빠른 재전송: 중복 ACK 세 개 ​

타임아웃만 기다리면 손실 하나에 RTO만큼, RFC 6298의 권고대로라면 적어도 1초 동안 멈출 수 있다. 그래서 TCP는 더 빠른 신호도 쓴다. 중간 세그먼트 하나가 빠진 채 뒤 세그먼트가 도착하면, 받는 쪽은 누적 규칙에 따라 빠진 자리 번호의 ACK를 되풀이해 보낸다. 이렇게 같은 번호로 거듭 오는 ACK를 중복 ACK(duplicate ACK)라고 한다.

text
보냄  1  2  3  4  5  6     (3번이 중간에 사라짐)
받음  1  2     4  5  6
ACK   2  3     3  3  3     ← 3번을 기다린다는 중복 ACK 세 개
보냄              3        ← 타임아웃 전에 3번만 다시 보냄

보내는 쪽은 중복 ACK를 세 개 받으면 타이머가 끝나기 전이라도 그 자리의 세그먼트를 바로 다시 보낸다. 이것을 빠른 재전송(fast retransmit)이라고 한다. 한두 개로 판단하지 않는 이유는 세그먼트의 도착 순서가 잠깐 뒤바뀌기만 해도 중복 ACK가 한두 개 생길 수 있기 때문이다.

흐름 제어: 수신 윈도우 ​

받는 쪽은 도착한 데이터를 수신 버퍼에 담아 두었다가, 애플리케이션이 읽어 가면 비운다. 애플리케이션은 느리게 읽는데 보내는 쪽이 계속 밀어 넣으면 버퍼가 넘쳐 데이터를 버려야 한다. 흐름 제어는 받는 쪽이 감당할 수 있는 만큼만 보내게 하는 장치다.

받는 쪽은 ACK를 보낼 때마다 헤더의 윈도우 필드에 버퍼의 남은 공간을 적는다. 이 값을 수신 윈도우(receive window, rwnd)라고 한다. 보내는 쪽은 ACK를 기다리지 않고 수신 윈도우 크기만큼 연달아 보낼 수 있고(실제 한도는 다음 문서에서 다룰 혼잡 윈도우(congestion window)와 비교해 더 작은 쪽이다), ACK가 올 때마다 보낼 수 있는 범위를 앞으로 민다. 이 방식이 슬라이딩 윈도우(sliding window)다.

text
[확인받음 ~1000][보냈고 ACK 대기 1001~3000][지금 보낼 수 있음 3001~5000][아직 못 보냄 5001~]
                └──────────────── 윈도우 4000바이트 ────────────────┘

세그먼트 하나마다 ACK를 기다리면 RTT 한 번에 세그먼트 하나밖에 못 보낸다. 슬라이딩 윈도우를 쓰면 RTT 한 번 동안 윈도우 크기만큼 보낼 수 있어 처리량(throughput)이 크게 는다. 받는 쪽 버퍼가 가득 차면 수신 윈도우 0을 알려 보내기를 멈추게 한다. 보내는 쪽은 그동안 작은 확인용 세그먼트를 가끔 보내 윈도우가 다시 열렸는지 묻고, 버퍼가 비면 받는 쪽이 새 윈도우 크기를 알려 전송이 다시 이어진다.

이어지는 내용은 TCP의 신뢰성과 혼잡 제어 2에서 다룬다.

면접 포인트 ​

  • "TCP는 어떻게 신뢰성을 보장하나요?" → 순서 번호, 누적 ACK, 타임아웃 재전송과 중복 ACK 세 개에 의한 빠른 재전송.
  • "RTO는 어떻게 정하나요?" → 측정한 RTT의 평균과 흔들림으로 계산. 너무 짧으면 괜한 재전송, 너무 길면 손실에서 오래 멈춤. 타임아웃마다 두 배.
  • "슬라이딩 윈도우를 왜 쓰나요?" → ACK를 기다리지 않고 수신 윈도우만큼 연달아 보내 RTT 한 번에 여러 세그먼트를 보낸다. 수신 윈도우 0이면 멈춘다.
  • ACK 번호를 "받은 마지막 바이트 번호"라고 말하면 틀린다. 다음에 받을 바이트 번호다.

참고

  1. RFC 6298의 계산식은 RTO = SRTT + max(G, 4 × RTTVAR)다. SRTT는 RTT의 이동 평균, RTTVAR는 RTT가 흔들리는 정도, G는 시스템 타이머의 최소 단위다. ↩︎

관련 문서