TCP, UDP
이 글은 제 개인적인 공부를 위해 작성한 글입니다. 틀린 내용이 있을 수 있고, 피드백은 환영합니다.
TCP (Transmission Control Protocol)
TCP는 신뢰성 있는 데이터 전송을 보장하는 연결 지향형 프로토콜이다. 데이터가 중간에 유실되거나 순서가 뒤바뀌는 것을 허용하지 않는 모든 서비스의 기반이 된다. 바이트 스트림 방식이라 데이터의 경계가 없다.
1. 연결 지향(Connection Oriented)
데이터를 주고받기 전에 송신측과 수신측이 서로 연결되어 있는지 확인하는 3-Way-Handshake 과정을 거친다. 연결을 끊을 때는 4-Way-Handshake 과정을 거친다.
1. 연결 지향 : 3-Way-Handshake
- 클라이언트가 서버에 연결을 요청한다. TCP 헤더의 SYN 비트를 1로 설정하여 보내고, Seq #를 초기화(x)한다.
- 서버가 연결이 가능할 경우, 클라이언트에게 연결 요청에 대한 응답을 한다. TCP 헤더의 ACK, SYN 비트를 1로 설정하여 보내고, Seq #를 초기화(y)한다.
- 클라이언트가 서버에게 연결을 확인한다. TCP 헤더의 ACK 비트를 1로 설정하여 보내고 이 과정 이후에 실제 데이터를 송수신한다.
1. 연결 지향 : 4-Way-Handshake
- 클라이언트가 서버에게 연결 종료를 요청한다. 소켓을 FIN_WAIT_1 상태로 전환하고, TCP 헤더의 FIN 비트를 1로 설정하여 보낸다.
- 서버가 클라이언트에게 연결 종료 요청에 대한 응답을 한다. 소켓을 연결 종료를 대기하는 CLOSE_WAIT 상태로 전환하고, TCP 헤더의 ACK 비트를 1로 설정하여 보낸다.
- 서버가 연결 종료를 위해 클라이언트에게 종료를 요청한다. 소켓을 LASK_ACK 상태로 전환하고, TCP 헤더의 FIN 비트를 1로 설정하여 보낸다.
- 클라이언트가 서버에게 연결 종료 요청에 대한 응답을 한다. TCP 헤더의 ACK 비트를 1로 설정하여 보낸다.
- 4단계의 ACK을 받은 서버는 소켓을 CLOSED 상태로 전환한다. 클라이언트도 maximum segment lifetime(MSL) * 2 만큼 기다린 후 소켓을 CLOSED 상태로 전환한다. 이는 4단계의 ACK이 클라에서 서버로 제대로 전송되지 않았을 경우를 대비하기 위해 기다리는 것이다. 4단계의 ACK이 유실될 경우, 서버는 ‘아 내가 보낸 FIN이 유실됐거나, 클라가 보낸 ACK이 유실됐구나’라고 판단하여 3단계의 FIN을 재전송한다. MSL의 2배인 이유는 서버가 다시 FIN을 클라이언트에게 재전송 + 클라이언트가 서버에게 ACK을 재전송하는 시간을 대기하기 위함이다.
2. 흐름 제어(Flow Control)
TCP는 송신측과 수신측의 데이터 처리 속도 차이를 제어한다. 수신자의 소켓의 버퍼를 보고 송신자의 윈도우 사이즈(전송량)를 조절하는 슬라이딩 윈도우(Sliding Window) 방식을 사용한다.
TCP 헤더에는 수신자의 비어있는 버퍼 크기인 window size 정보가 포함되어 있다. 수신자 버퍼에 데이터가 쌓이면 어플리케이션 계층에서 데이터를 가져가는데, 가져가는 속도보다 버퍼에 데이터가 쌓이는 속도가 더 빠르면 데이터가 유실될 가능성이 있다. 하지만 TCP는 데이터 무결성(Data Integrity)을 보장하기 때문에, 이를 조절하는 것이다. (수신자가 송신자에게 명령)
3. 혼잡 제어(Congestion Control)
네트워크 상의 혼잡을 줄이기 위해 패킷의 전송량을 조절하는 것이다. 라우터의 버퍼가 유한하기 때문에 혼잡이 발생할 수 있고, 이로 인해 패킷의 지연이나 유실이 일어날 수 있으니 혼잡 제어가 필요하다.
데이터의 유실을 막기 위해 송신량을 조절한다. 라는 점에서 흐름 제어와 유사하지만, 흐름 제어는 송신자와 수신자 간(End-to-End) 제어임에 비해 혼잡 제어는 라우터와 같은 네트워크 내에서 데이터의 유실이 발생하지 않도록 조절한다. 또한 흐름 제어는 데이터가 유실되기 전에 제어가 가능하지만, 혼잡 발생으로 인한 데이터 유실은 피할 수 없고 더 심한 혼잡이 발생하지 않게 제어하는 것이다.
혼잡 제어의 자세한 내용는 길어서 따로 글을 작성할게요.
4. 신뢰할 수 있는 데이터 전송(Reliable Data Transfer:RDT)
TCP는 하부 계층인 IP의 신뢰할 수 없는 서비스(패킷 유실/순서 뒤바뀜) 위에서 작동하면서도, 애플리케이션 계층에는 신뢰할 수 있는 데이터 전송 서비스를 제공한다. 이를 위해 다음과 같은 기술들을 사용한다.
- 파이프라인 세그먼트 : ACK을 매번 기다리지 않고 여러 세그먼트를 연속으로 전송하여 전송 효율을 높인다.
- 누적 ACK : 수신 측은 받은 세그먼트에 대해 ACK을 보내는데, 마지막으로 성공한 바이트 번호 다음의 기대 번호를 ACK으로 보낸다. (ex: ACK 100은 99까지 받았으니 100을 달라는 뜻)
- 단일 재전송 타이머 : 전송된 세그먼트 중 가장 오래돼었고, 아직 ACK을 받지 못한 하나의 세그먼트를 위해서 타이머를 구동한다. 이 타이머가 timeout되면 해당 세그먼트만 재전송한다.
TCP 송신 측의 상태 머신(FSM)은 크게 3가지 이벤트로 움직인다.
애플리케이션 계층으로부터 데이터 수신
데이터를 받으면 시퀀스 번호(Seq #)를 부여하여 세그먼트를 생성한다. 시퀀스 번호는 해당 세그먼트에 담긴 첫 번째 바이트의 바이트 스트림 번호이다. 만약 현재 실행 중인 타이머가 없다면, 방금 보낸 세그먼트를 위해 타이머를 시작한다.
타임아웃 발생
타이머가 timeout되면, timeout을 발생시킨 가장 오래 대기한 그 세그먼트를 재전송한다. 재전송 후 타이머를 다시 시작한다.
수신 측으로부터 ACK 수신
해당 ACK 번호보다 작은 모든 세그먼트들을 전송 성공 상태로 업데이트한다. 아직 ACK을 받지 못한 세그먼트가 남아있다면 타이머를 다시 킨다. 모든 세그먼트가 ACK을 받았다면 타이머를 중지한다.
4. RDT : 재전송 시나리오
단순 패킷 유실에 의한 재전송
- 송신 측이 Seq 92를 보냈고 잘 도착했으나, 수신 측의 ACK 100이 유실됐다.
- 송신 측이 timeout되면, Seq 92가 도착했는지 여부를 알 수 없으니 Seq를 재전송한다.
조기 timeout으로 인한 재전송
- 송신 측이 Seq 92, 100을 보냈고 잘 도착했으나, 수신 측의 ACK이 늦게 도착해 timeout 된다.
- 송신 측이 Seq 92를 다시 보내보고, 수신 측에서 ACK 120 오는 것을 확인하여 ‘아 아까 보낸 100까지 잘 갔구나’를 알 수 있다.
- 수신 측은 Seq 100까지 잘 받았고 누적 ACK이기에 ACK 120을 보내는 것이다.
누적 ACK 덕분에 재전송을 피하는 경우
- 송신 측이 Seq 92, 100을 보냈고 잘 도착했으나, 수신 측의 ACK 100, 120 중 100만 유실되었다.
- 송신 측은 ACK 100이 유실되었지만, ACK 120이 왔으니 120의 이전 것들까지 받았음을 알 수 있다.
- 누적 ACK을 사용하기에 Seq 120부터 전송할 수 있다.
4. RDT : 빠른 재전송(Fast Retransmit)
timeout까지의 시간은 생각보다 길기에, 패킷 유실을 빠르게 알아채기 위해 중복 ACK을 사용한다. 송신 측이 첫 번째 ACK을 받은 이후 동일한 중복 ACK을 3번 더 받으면, 타이머가 아직 끝나지 않았더라도 ‘네트워크는 괜찮은데 특정 패킷 하나가 유실됐구나’라고 생각하여 그 세그먼트를 즉시 재전송한다.
TCP 단점
- 연결 설정/해제 과정에서 패킷 검증과 오버헤드 때문에 UDP에 비해 상대적으로 느리다.
- 헤더 크기가 UDP에 커서 패킷당 데이터 효율이 낮다.
UDP(User Datagram Protocol)
UDP는 연결을 확립하지 않고 신뢰성을 보장하지 않는 프로토콜이다. 패킷 전송 중 손실이 발생하면 손실된 상태로 처리하고, 순서도 뒤바뀔 수 있다. 손실이 일어나도 괜찮고, 속도가 중요한 서비스에 주로 사용한다.
- 비연결 지향 : TCP와 다르게 Handshake 과정이 없다. 소켓을 열고 목적지 주소로 데이터를 바로 보낸다.
- 흐름 제어, 혼잡 제어, 재전송이 없다. 따라서 네트워크 지연이 거의 없고 높은 성능을 보여준다.
- 헤더 크기가 딱 8바이트로 고정되어 있어 패킷 오버헤드가 적다.
- TCP는 바이트 스트림 단위라 데이터의 경계가 없지만, UDP는 보낸 단위(Datagram) 그대로 수신 측에 도착하므로 데이터 경계가 유지된다.
UDP 단점
- 패킷이 도중에 유실되어도 알 수 없고, 패킷 도달 순서도 바뀔 수 있다.
- 신뢰성이 필요한 경우, 개발자가 애플리케이션 계층에서 직접 신뢰성 로직을 구현해야 한다.
정리
| 특성 | TCP | UDP |
|---|---|---|
| 연결 방식 | 연결 지향형 (3-Way Handshake) | 비연결 지향형 |
| 신뢰성 / 순서 | 보장함 (유실 시 재전송) | 보장하지 않음 |
| 전송 순서 | 보낸 순서대로 도착 보장 | 순서가 바뀔 수 있음 (독립적 패킷) |
| 제어 기능 | 흐름 제어, 혼잡 제어 존재 | 없음 |
| 헤더 크기 | 20 ~ 60 바이트 (가변) | 8 바이트 (고정) |
| 속도 | 상대적으로 느림 | 상대적으로 매우 빠름 |
| 데이터 경계 | 없음 (Byte Stream) | 있음 (Datagram) |
| 주요 활용처 | - 웹(HTTP/HTTPS) - 파일(FTP) - 이메일(SMTP) - 로그인/결제 세션 | - 실시간 스트리밍 - VoIP(인터넷 전화) - DNS - 멀티플레이어 게임 패킷 |





