STUDY NOTE · 개념 정리
TCP vs UDP 정리: 3-way·4-way 핸드셰이크, 흐름 제어와 혼잡 제어
Network
TCP는 연결을 맺고, 순서와 도착을 보장하는 전송 계층 프로토콜이고, UDP는 연결 없이 데이터그램을 던지기만 한다. TCP는 믿을 수 있는 대신 느리고 무겁고, UDP는 가볍고 빠른 대신 신뢰성은 애플리케이션이 직접 챙겨야 한다.
1. 한눈에 비교
| 구분 | TCP | UDP |
| 연결 | 연결형 (핸드셰이크 후 전송) | 비연결형 |
| 신뢰성 | 재전송·확인 응답(ACK)으로 보장 | 보장 안 함 (유실 가능) |
| 순서 | 보장 (시퀀스 번호) | 보장 안 함 |
| 데이터 단위 | 바이트 스트림 (경계 없음) | 데이터그램 (보낸 단위 그대로) |
| 흐름·혼잡 제어 | 있음 | 없음 |
| 헤더 크기 | 20~60바이트 | 8바이트 |
| 통신 형태 | 1:1 | 1:1, 1:N (브로드캐스트·멀티캐스트) |
| 대표 용도 | HTTP/1.1·2, 이메일, 파일 전송, SSH | DNS, 실시간 영상·음성, 게임, QUIC(HTTP/3) |
2. 3-way 핸드셰이크 (연결 맺기)
- 클라이언트 → 서버: SYN (내 시작 시퀀스 번호는 x)
- 서버 → 클라이언트: SYN + ACK (x+1 받을 준비 됐고, 내 시작 번호는 y)
- 클라이언트 → 서버: ACK (y+1 받을 준비 됐음) → 연결 성립(ESTABLISHED)
왜 2번이 아니라 3번인가? 양쪽 모두 '내가 보낸 것이 상대에게 닿았다'와 '상대의 시작 번호'를 확인해야 하기 때문이다. 2번이면 서버는 자기 SYN이 클라이언트에 닿았는지 모른다. 또 네트워크에 오래 떠돌던 옛 SYN이 늦게 도착해도, 클라이언트가 3번째 ACK를 보내지 않으니 잘못된 연결이 생기지 않는다.
3. 4-way 핸드셰이크 (연결 끊기)
- 클라이언트 → 서버: FIN (나는 더 보낼 게 없음)
- 서버 → 클라이언트: ACK (알았음, 하지만 나는 아직 보낼 게 남았을 수 있음)
- 서버 → 클라이언트: FIN (나도 다 보냄)
- 클라이언트 → 서버: ACK → 클라이언트는 TIME_WAIT에서 잠시 기다렸다 닫음
| 질문 | 답 |
| 왜 4번인가 | TCP는 양방향이라 각 방향을 따로 닫는다. 서버는 FIN을 받은 뒤에도 남은 데이터를 보낼 수 있어서 ACK와 FIN이 따로 간다 (반 닫힘, half-close) |
| TIME_WAIT는 왜 있나 | 마지막 ACK가 유실되면 서버가 FIN을 다시 보내는데, 그걸 받아 줄 수 있어야 한다. 또 늦게 도착한 옛 패킷이 같은 포트의 새 연결에 섞이지 않게 한다. 보통 2×MSL(최대 세그먼트 수명) 동안 기다림 |
| TIME_WAIT가 많이 쌓이면 | 연결을 먼저 끊는 쪽에 생긴다. 짧은 연결을 많이 맺는 서버라면 Keep-Alive·커넥션 풀로 연결을 재사용한다 |
4. 신뢰성: 시퀀스 번호, ACK, 재전송
- 보내는 바이트마다 시퀀스 번호를 붙이고, 받는 쪽은 '다음에 받을 번호'를 ACK로 알려 준다.
- 정해진 시간(RTO) 안에 ACK가 안 오거나, 같은 ACK가 3번 겹쳐 오면(중복 ACK) 유실로 보고 다시 보낸다(빠른 재전송).
- 순서가 뒤바뀐 세그먼트는 받는 쪽 버퍼에서 번호대로 맞춰서 애플리케이션에 넘긴다.
5. 흐름 제어 vs 혼잡 제어
| 구분 | 흐름 제어 (Flow Control) | 혼잡 제어 (Congestion Control) |
| 보호 대상 | 받는 쪽 (수신 버퍼가 넘치지 않게) | 네트워크 (라우터 큐가 넘치지 않게) |
| 판단 기준 | 수신자가 알려 주는 윈도우(rwnd) | 송신자가 추정하는 혼잡 윈도우(cwnd) |
| 방식 | 슬라이딩 윈도우: ACK 없이 보낼 수 있는 양을 rwnd로 제한 | 느린 시작 → 혼잡 회피, 유실이 보이면 cwnd 줄이기 |
실제로 보낼 수 있는 양은 min(rwnd, cwnd)이다.
- 느린 시작(Slow Start): cwnd를 작게 시작해 ACK마다 늘려서, RTT마다 대략 두 배가 된다.
- 혼잡 회피: 임계값(ssthresh)을 넘으면 RTT마다 1 MSS씩 천천히 늘린다(AIMD의 '덧셈 증가').
- 유실 감지: 타임아웃이면 cwnd를 처음부터, 중복 ACK 3번이면 절반으로 줄인다(빠른 회복, '곱셈 감소').
- 요즘 리눅스 기본은 CUBIC이고, 구글의 BBR은 유실 대신 대역폭과 RTT를 재서 속도를 정한다.
6. UDP는 언제 쓰나
- 늦게 오느니 안 오는 게 나은 데이터: 실시간 음성·영상, 게임 위치 동기화. 지난 프레임을 재전송받아 봐야 쓸 데가 없다.
- 요청 하나에 응답 하나로 끝나는 짧은 질의: DNS. 핸드셰이크 비용이 질의보다 크다.
- 신뢰성을 직접 설계하고 싶을 때: QUIC(HTTP/3)는 UDP 위에서 재전송·암호화·스트림 다중화를 직접 구현해서, TCP의 Head-of-Line 블로킹(앞 패킷 하나가 유실되면 뒤가 전부 대기)을 피한다.
- WebRTC 데이터 채널도 '순서 보장 끔 + 재전송 끔'으로 설정하면 UDP처럼 쓸 수 있다.
7. 정리
- TCP = 연결 + 순서 + 신뢰성 + 흐름·혼잡 제어, 대신 지연과 오버헤드.
- UDP = 헤더 8바이트, 연결 없음, 보장 없음, 대신 빠르고 단순.
- 연결은 3번(SYN, SYN-ACK, ACK), 끊기는 4번(양방향을 따로 닫기), 먼저 끊는 쪽에 TIME_WAIT.
- 흐름 제어는 받는 사람, 혼잡 제어는 네트워크를 지킨다.