TCP와 UDP는 둘 다 데이터를 전송하는 프로토콜이다. 차이는 단순하다. TCP는 데이터가 제대로 도착했는지 확인하고, UDP는 확인하지 않는다.
TCP는 데이터를 보내기 전에 먼저 연결을 확인한다. 이를 3-way handshake라고 한다.
Client → SYN → Server (나 연결할게)
Client ← SYN+ACK ← Server (알겠어, 나도 준비됐어)
Client → ACK → Server (확인)
연결이 확인된 후에야 데이터를 주고받는다. HTTP API 통신, 소켓 통신에 주로 쓰이며, keep-alive를 통해 handshake 횟수를 줄이기도 한다.
연결을 끊을 때는 4-way handshake를 사용한다.
Client → FIN → Server (나 끊을게)
Client ← ACK ← Server (알겠어)
Client ← FIN ← Server (나도 끊을게)
Client → ACK → Server (확인)
Client가 마지막 ACK를 보내고 나서 바로 연결을 끊지 않는다. 일정 시간 대기 상태로 있는데, 이유가 두 가지다.
첫 번째: 마지막 ACK 유실 대응
Client의 ACK가 유실되면 Server는 FIN을 재전송한다. Client가 이미 연결을 끊은 상태라면 이 재전송을 받을 수 없다. TIME_WAIT 동안 대기하면서 재전송된 FIN을 받아 다시 ACK를 보낼 수 있다.
두 번째: 이전 연결의 패킷 오염 방지
TCP는 연결을 (출발지 IP, 출발지 포트, 목적지 IP, 목적지 포트) 4개 조합으로 식별한다. TIME_WAIT 없이 같은 조합으로 새 연결이 즉시 열리면, 이전 연결에서 네트워크에 떠돌던 지연 패킷이 새 연결의 정상 데이터로 오인될 수 있다.
UDP는 연결 확인 없이 데이터를 바로 전송한다. 패킷이 유실되어도 재전송하지 않는다.
영상 스트리밍이 UDP를 쓰는 이유가 여기에 있다. 실시간 영상은 이미 지나간 프레임을 다시 받는 것보다, 현재 프레임을 빠르게 받는 게 중요하다. 패킷 손실은 버퍼링으로 나타난다.
TCP 기반 HTTP에는 구조적인 문제가 있었다.
1번부터 10번까지 데이터를 보내는 상황에서 3번이 유실됐다고 하면, TCP는 순서를 보장해야 하기 때문에 4번~10번을 이미 받았더라도 3번이 재전송될 때까지 상위 레이어에 데이터를 올려보내지 못한다.
이걸 HOL Blocking(Head-of-Line Blocking) 이라고 한다.
| 버전 | HOL Blocking |
|---|---|
| HTTP/1.1 | 요청 자체를 순서대로만 처리 |
| HTTP/2.0 | 스트림 다중화로 개선, 단 TCP 레벨 블로킹은 여전히 존재 |
| HTTP/3.0 | 해결 |
HTTP/3.0은 UDP 기반의 QUIC 프로토콜을 사용한다. UDP라서 신뢰성이 없다고 생각할 수 있지만, QUIC이 애플리케이션 레벨에서 재전송과 순서 보장을 직접 구현한다.
핵심은 스트림 독립성이다. 3번 스트림에서 손실이 나도 다른 스트림은 영향받지 않고 계속 전달된다. TCP에서는 같은 연결 내 모든 데이터가 하나의 순서 흐름을 공유하기 때문에 이게 불가능하다.
TCP가 전송량을 조절하는 방법이 두 가지 있다.
흐름 제어 — 수신자 기준
수신자(클라이언트)가 자신의 버퍼 여유를 수신 윈도우 값으로 송신자에게 알린다. 송신자는 이 값을 초과해서 보내지 않는다.
혼잡 제어 — 네트워크 경로 기준
클라이언트와 서버 사이에는 여러 라우터가 있다. 수신자 버퍼는 여유 있어도 중간 라우터에 트래픽이 몰리면 패킷이 버려진다.
TCP는 패킷 손실을 "네트워크가 혼잡하다"는 신호로 받아들이고 혼잡 윈도우 크기를 줄인다. 처음 연결할 때는 작게 시작해서 문제가 없으면 2배씩 늘려가는 방식을 슬로우 스타트라고 한다.
실제 전송량은 두 윈도우 중 더 작은 값으로 결정된다.
실제 전송량 = min(수신 윈도우, 혼잡 윈도우)