본문 바로가기
📖 Computer Science/네트워크

[CS Study] 네트워크 (2) - TCP와 UDP

by 헤이즐넛 좋아하는 개발자 2025. 5. 7.

시작하기 전에

저번에 네트워크 계층을 살펴봤으니 TCP와 UDP로 넘어가 봅시다.

TCP와 UDP는 전송 계층에서 사용하는 프로토콜이고, TCP는 신뢰성을 보장하는 방식임을 앞의 글에서 정리했습니다. TCP에 대해 조금 더 자세히 정리해봅시다.


TCP

TCP는 연결형 서비스를 지원하고 데이터의 신뢰성을 보장한다.

다음과 같은 특징이 있다 (아래의 가상 회선 방식 정의를 참고하면서 특징들을 보면 모든 게 자연스럽다):

  • 송신부와 수신부의 연결을 확인하는 연결형 서비스다.
  • 패킷 교환 방식은 패킷이 전달되는 회선이 정해져 있는 가상 회선 방식이다.
  • 패킷의 전송 순서가 보장된다
  • 패킷의 수신 여부를 확인한다.
  • 송신부와 수신부는 1:1 통신을 한다.
  • 데이터 손실이 없음을 보장하므로 신뢰성이 높다.
  • 데이터의 송수신 속도가 느리다.

추가로 패킷 교환 방식에는 2가지가 있다.

  • 가상 회선 방식: 데이터를 주고받기 전에 패킷 전송 경로인 가상 회선을 설정하여 모든 패킷을 같은 경로로 전송한다.
  • 데이터그램 방식: 패킷마다 최적의 경로로 전송한다. 송신부에서 보낸 패킷의 순서와 수신부에서 받은 패킷의 순서가 다를 수 있다.
그럼 TCP가 신뢰성을 어떻게 보장하는지 구체적으로 살펴보자.

TCP 핸드셰이킹

TCP에서는 연결형 서비스를 지원하기 위해 송신부와 수신부를 연결하는 과정을 거친다. 앞서 나온 세션의 개념이 다시 등장한다.

연결을 시작할 때는 3-way 핸드셰이킹을, 연결을 종료할 때는 4-way 핸드셰이킹을 한다.

핸드셰이킹 과정에서는 송신부와 수신부 간 연결을 제어 및 관리하도록 플래그(flag) 값을 주고받는다.

자주 사용하는 플래그는 SYN, FIN, ACK이다.

  • SYN: Synchronization(동기화)의 약자로, 연결 생성 시 사용
  • FIN: Finish(종료)의 약자로, 연결 종료 시 사용
  • ACK: Acknowledgement(승인)의 약자로, 전송한 데이터를 수신자가 받았음을 알려 줄 때 사용
3-way 핸드셰이킹, 4-way 핸드셰이킹을 상세히 살펴보자.

3-way 핸드셰이킹

데이터를 주고받기 전에 송신부와 수신부 간 세션을 수립하는 과정

  1. 송신부가 수신부에 연결 요청을 위해 SYN 메시지와 임의의 숫자 N을 보낸다. 수신부의 응답이 오기 전까지 SYN_SENT 상태가 된다.
  2. 수신부가 송신부로부터 SYN 메시지를 받으면 연결 요청을 수락하는 의미의 ACK 메시지와 N에 1을 더한 N+1을 함께 보낸다.
    수신부에서도 송신부에 연결 확인을 위해 SYN 메시지와 임의의 숫자 M을 함께 보낸다.
    송신부의 응답이 오기 전까지 SYN_RECEIVED 상태가 된다.
  3. 송신부가 수신부에 SYN+ACK 메시지를 받으면 연결이 성립되었다는 의미의 ESTABLISHED 상태가 된다.
    메시지에 대한 응답으로 ACK 메시지와 M+1, 원한다면 송신부에서 전송하려는 데이터까지 함께 보낸다.
    수신부도 송신부로부터 ACK 메시지를 받으면 ESTABLISHED 상태가 된다.

이렇게 3번의 요청과 응답을 반복하여 연결을 확인한 후 본격적으로 데이터 통신을 하게 된다.

 

cf) N, M, N+1, M+1이 가지는 의미는 뭘까?

N, M은 송·수신부의 초기 시퀀스 번호이고,
SYN 플래그 자체도 데이터 1개 크기로 취급되기 때문에 N+1, M+1은 SYN 메시지까지 포함해 이 번호부터 시작하면 된다는 뜻이다.


4-way 핸드셰이킹

세션 연결을 해제할 때 이뤄지는 과정

  1. 송신부가 수신부에 연결 종료 요청을 위해 FIN 메시지를 보내고 FIN_WAIT1 상태가 된다.
  2. 수신부가 송신부로부터 FIN 메시지를 받으면 응답으로 ACK 메시지를 보내고 CLOSE_WAIT 상태가 된다.
    수신부는 메시지를 보낸 후 앱을 종료하는 등 연결을 종료하기 위한 작업을 한다.
    송신부는 수신부에서 보낸 ACK 메시지를 받고 FIN_WAIT2 상태가 된다.
  3. 수신부가 연결을 종료할 준비가 끝나면 송신부에 FIN 메시지를 보내고 LAST_WAIT 상태가 된다.
  4. 송신부는 수신부의 FIN 메시지에 대한 응답으로 ACK 메시지를 보내고 TIME_WAIT 상태가 된다.
    일정 시간이 지나면 CLOSED 상태가 된다.
    일정 시간 동안 TIME_WAIT 상태를 유지하는 이유는 FIN 메시지 전에 보낸 패킷이 FIN 메시지 수신보다 늦어 발생하는 패킷 유실에 대비하기 위함이다. 또한, 수신부에 ACK 메시지가 제대로 전달되지 않아 연결 해제가 이뤄지지 않는 경우도 대비한다.
    수신부는 송신부로부터 ACK 메시지를 받고 CLOSED 상태가 된다.
TCP 데이터의 신뢰성을 보장하기 위한 3가지 제어 방법이 있다. 알아보자.

흐름 제어

송신부와 수신부에서 데이터 처리 속도의 차이 때문에 생기는 데이터 손실을 방지하는 방식이다.

대표적인 2가지 방법이 있다.

 

1. 정지-대기

송신부에서 패킷을 보낸 후 수신부로부터 ACK 메시지를 받기 전까지 다음 데이터를 전송하지 않는 방식이다.

일정 시간 동안 ACK 메시지를 받지 못하면 패킷을 재전송한다.

간단하지만, ACK 메시지를 받아야만 다음 패킷을 보낼 수 있으니 비효율적이다.

 

2. 슬라이딩 윈도우

송신부에서 수신부로부터의 ACK 메시지를 수신하지 않아도 윈도우 크기만큼의 패킷은 전달하도록 하는 방식이다.

작동 방식:

  1. 윈도우 크기가 4일 때 송신부에서 데이터 1과 2를 보내고, 3과 4는 보낼 수 있지만 아직 보내지 않은 상태다.
  2. 수신부에서 데이터 1과 2를 받았다는 ACK 메시지를 보낸다.
  3. 송신부는 수신부가 데이터 1과 2를 받았음을 확인하고 윈도우를 2칸 이동한다. 따라서 3부터 6까지 보낼 수 있는 상태가 된다.
    송신부에서 데이터 3, 4, 5를 보낸다.
  4. 수신부에서 데이터 3과 4를 받았다는 ACK 메시지를 보낸다.
    송신부는 수신부에서 데이터 3과 4를 받았다는 ACK 메시지를 받았지만, 데이터 5를 받았다는 ACK 메시지를 받지 못해서 윈도우를 2칸만 이동한다. 이제 데이터 7과 8도 보낼 수 있는 상태가 된다.

수신 여부와 관계없이 일정 크기의 데이터를 보낼 수 있게 함으로써 정지-대기 방식의 단점을 보완한다.


혼잡 제어

송신부의 데이터 전달 속도와 네트워크 속도 차이로 발생하는 데이터 손실을 방지하는 방식이다.

혼잡은 네트워크에 패킷이 과도하게 증가하는 현상이다. 혼잡이 발생해 네트워크에 패킷이 쌓이면서 송신부에서 메시지 전송에 실패했다고 판단해 패킷을 재전송하는데, 이는 혼란을 야기한다.

따라서 TCP는 혼잡 윈도우의 크기를 조절해 혼잡에 대응한다.

혼잡 제어에는 다음과 같은 방법이 있다:

 

1. AIMD(Additive Increase Multiplicative Decrease)

  • 데이터를 전달할 때 혼잡 윈도우의 크기를 선형적으로 증가시키다가, 데이터 손실이 발생하면 1/2로 줄이는 방식이다.
  • 여러 송신부가 네트워크 대역폭을 공평하게 사용할 수 있다는 장점이 있다.
  • 하지만 윈도우 크기 증가 폭 대비 감소 폭이 크기 때문에 네트워크 대역폭을 넓게 사용하기까지 시간이 오래 걸린다는 단점이 있다.

2. 느린 시작

  • 윈도우 크기 1부터 시작해 ACK 메시지를 수신할 때마다 윈도우 크기를 1씩 늘려나가다가, 혼잡이 발생하면 1로 줄이는 방식이다.
  • 그럼 선형적으로 증가하는 거 아닌가 혼동할 수 있다. 느린 시작은 수신자로부터 패킷 1개에 대한 ACK 메시지를 받으면 다음에는 2개를 보낼 수 있고, 패킷 2개에 대한 ACK 메시지를 받으면 다음에는 4개로 보낼 수 있다. 따라서 윈도우 크기가 지수적으로 증가한다.
  • 패킷을 처음부터 최대 개수만큼 보내는 게 아니라 1개부터 점차 늘려나가기 때문에 느린 시작 이라는 이름이 붙었다.
  • 전송 가능한 패킷 수가 지수적으로 증가하기 때문에 AIMD 방식에서 초기에 전송 가능한 패킷 수가 적다는 단점을 보완할 수 있다.

3. 혼잡 회피

  • 윈도우 크기가 지수 함수 형태로 증가하다가 혼잡이 발생하는 것을 방지하기 위해 윈도우 크기에 대한 임계점을 정하는 방식이다.
  • 윈도우 크기가 임계점에 도달하면 선형적으로 증가시킨다.
  • ACK 메시지를 받지 못해 타임아웃이 발생하면 윈도우 크기의 절반을 임계점으로 설정하고, 윈도우 크기를 초깃값으로 바꾼다.

4. 빠른 회복

  • 혼잡이 발생하면 혼잡 윈도우 크기를 절바능로 줄인 후 선형적으로 증가하는 방식이다. 즉, 혼잡이 발생하면 AIMD 방식과 동일하다.

5. 빠른 재전송

  • Duplicate ACK가 3번 발생하면 윈도우 크기를 1/2로 줄인다. 그 뒤 ACK 메시지를 받으면 다시 윈도우 크기를 키우는 방식이다.
  • Duplicate ACK는 패킷이 순서대로 도착하지 않아서 받아야 할 차례의 패킷을 ACK 메시지와 함께 요청하는 것을 의미한다.
  • 예를 들어, 송신부에서 패킷 0부터 5까지 보냈다. 수신부에는 패킷 2가 오지 않았다. 수신부에서는 패킷 3, 4, 5에 대해 패킷 2를 먼저 보내라고 응답한다. 해당 응답(Duplicate ACK)이 3번 반복되면 송신측에서는 패킷 2를 재전송한다. 이때 윈도우 크기는 1/2로 줄어든다.

오류 제어

데이터에 오류 또는 유실이 발생할 때 데이터의 신뢰성을 보장하기 위해 오류를 제어하는 방식이다.

데이터에 오류 또는 유실 발생을 인지하는 경우는 다음과 같다:

  • 수신부로부터 잘못된 데이터를 받았다는 응답인 NAK(Negative Acknowledge) 메시지를 보낸 경우
  • 3 Duplicate ACK가 발생할 때
  • 수신부로부터 ACK 메시지를 받지 못해 타임아웃이 발생할 때

오류 제어에는 다음과 같은 방법이 있다:

 

1. 정지-대기

  • 흐름 제어에서도 동일한 방식이 있었다.
  • 마찬가지로 송신한 패킷에 대한 ACK 메시지를 받지 못해 타임아웃이 나면 패킷을 재전송하는 방식이다.
  • 송신부에서 데이터를 1개만 보내고 메시지를 기다려야 하기 때문에 아래의 예시처럼 ARQ(Automatic Repeat Request, 재전송 요청) 방식이 사용된다.

2. Go-Back-N ARQ

  • 송신부에서 연속적으로 데이터를 보냈을 때 누락된 데이터가 있으면 해당 데이터'부터' 재전송하는 방식이다.
  • 예를 들어, 송신부에서 패킷 0부터 2까지 보냈을 때 수신부에서 ACK 3 메시지를 보내면 송신부는 다음에 패킷 3부터 보낸다.
    패킷 3부터 5까지 보냈을 때 ACK 4 메시지를 받으면 다음에 패킷 4와 5를 재전송한다.
  • 중간 하나만 틀려도 뒤까지 몽땅 보내야 하는 단점이 있다. 그래서 Selective-Repeat ARQ가 나온다.

3. Selective-Repeat ARQ

  • 송신부에서 연속적으로 데이터를 보냈을 때 누락된 데이터가 있으면 해당 데이터'만' 재전송하는 방식이다.
  • 예를 들어, 수신부에서 패킷 0부터 2까지 받으면 패킷 3을 보내라는 의미로 ACK 3 메시지를 보낸다.
    패킷 3부터 5까지 보냈을 때 패킷 4를 받지 못하면 패킷 4를 보내라는 의미로 ACK 4 메시지를 보낸다.
    송신부는 패킷 4를 다시 보내고, 이를 받은 수신부는 패킷 6을 요청한다.
  • 특정 패킷만을 재전송한다는 점이 효율적으로 보이지만, 패킷 순서를 재정렬하는 로직이 필요하다는 단점이 있다.
이제 UDP로 넘어갑시다. TCP와 무엇이 다른지 살펴봅시다.

UDP(User Datagram Protocol, 사용자 데이터그램 프로토콜)

송신부와 수신부 간 연결을 지원하지 않고 데이터그램 형태의 통신을 지원한다.

신뢰성이 낮지만 속도가 빠르다는 단점이 있다.

다음과 같은 특징이 있다 (TCP의 특징과 비교하면서 보자):

  • 송신부와 수신부의 연결이 보장되지 않는 비연결형 서비스다.
  • 패킷이 서로 다른 회선으로 교환될 수 있는 데이터그램 패킷 교환 방식이다.
  • 패킷의 전송 순서가 보장되지 않는다.
  • 패킷의 수신 여부를 확인하지 않는다.
  • 송신부와 수신부가 1:1, 1:N, N:N 통신이 모두 가능하다.
  • 데이터의 신뢰성이 낮다.
  • 데이터의 송수신 속도가 빠르다.
그럼 UDP에서는 오류 검출을 어떻게 할까?

UDP의 오류 검출

UDP는 최소한의 신뢰성 보장을 위해 체크섬(checksum) 방식으로 오류를 검출할 수 있다. (선택 사항이다)

체크섬 방식이 뭔지 예시와 함께 보자:

  • 송신 측에서 모든 데이터를 비트로 더한다. -> 데이터에 1011과 1100이 있으면 더해서 10111
  • 결과 값의 하위 4비트만 남긴다. -> 데이터 합: 0111
  • 그걸 반전(~)시켜서 체크섬 값으로 만든다. -> 체크섬: 1000
  • 데이터+체크섬을 수신 측으로 보낸다. -> 0111, 1000
  • 수신 측에서 수신한 데이터들과 체크섬을 모두 더한다.
  • 모든 비트가 1이 되면(1111), 오류 없음
  • 아니라면, 어떤 데이터가 손실되었는지 계산한다.

하지만, 데이터의 값을 더해 확인하는 방식이라서 데이터의 순서가 바뀌거나 오류가 발생해도 체크섬이 같은 경우가 있어 오류를 100% 검출할 수 있는 것은 아니다.

다음으로는 HTTP로 넘어가보자.