TCP 란?
프로토콜 계층

- TCP 는 연결 지향형 프로토콜이다.
- TCP 는 가상 회선을 만들어 신뢰성을 보장하도록 하는 프로토콜로 속도가 느리다.
- TCP 는 파일전송과 같은 신뢰성이 중요한 서비스에 사용된다.
TCP (Transmission Control Protocol) 특징
- 연결지향 - TCP 3 way handshake (가상 연결)

- 데이터 전달을 보증한다.
- 메시지를 받지 못했을 경우 데이터가 전달되지 않았다는 사실을 알 수 있다.

- 메시지를 받지 못했을 경우 데이터가 전달되지 않았다는 사실을 알 수 있다.
- 패킷의 순서를 보장한다.

TCP 3 way handshake
- TCP 가 가상회선을 수립하는 단계이다.
- 클라이언트는 서버에 요청을 전송할 수 있는지, 서버는 클라이언트에게 응답을 전송할 수 있는지 확인하는 과정이다.
- SYN ACK 패킷을 주고받으며 임의의 난수로 SYN 플래그를 전송하고 ACK 플래그에 1을 더한값을 전송한다.
TCP 4 way handshake
- TCP 연결을 해제하는 단계로 클라이언트는 서버에게 연결 해제를 통지하고 서버가 이를 확인하고 클라이언트에게 이를 받았음을 전송해주고 최종적으로 연결이 해제된다.
- 단, 서버에서 소켓이 닫혔다고 통지해도 클라이언트 측에서는 일정시간 대기하는데 이는 혹시나 패킷이 나중에 도착할 수도 있기 때문이다.
흐름 제어
흐름 제어 (flow control) = 수신측이 감당 못 할 속도로 송신측이 보내는 것을 막는 메커니즘. “받는 쪽 버퍼 넘침” 방지.
- 문제 = 송신측이 빠르고 수신측이 느리면 수신 TCP buffer 가 가득 참 → 이후 도착 패킷은 drop → 재전송 → 낭비.
- 해법 = 수신측이 매 ACK 에 receive window (rwnd) 를 실어 “나 지금 이만큼 더 받을 수 있다” 통보. 송신측은 그 window 이내로만 in-flight 유지.
- window = 수신측 TCP buffer 의 남은 여유 공간. 가변. 아래
### Sliding Window·### Window-Size가 이 메커니즘. - 극단 = 수신 buffer 가 0 이면 window 0 통보 = Zero-Window. 송신측은 멈추고 주기적으로 window probe 를 보내며 회복 대기. (위
## 대표적인 TCP 장애 유형의 Zero-Window 참고)
→ 흐름 제어 = 수신측 capacity 에 맞춤. 1:1 (송신↔수신) 관계.
혼잡 제어
혼잡 제어 (congestion control) = 네트워크(중간 경로)가 감당 못 할 속도로 보내는 것을 막는 메커니즘. “네트워크 혼잡” 방지.
- 흐름 제어와 다른 점 = 흐름 제어는 수신측 버퍼, 혼잡 제어는 중간 네트워크(라우터 큐) 가 대상.
- 송신측이 congestion window (cwnd) 를 따로 유지. 실제 송신량 =
min(rwnd, cwnd). - 혼잡 신호 = 패킷 손실 (timeout 또는 dup ACK 3회). 손실 = 어딘가 라우터 큐가 넘쳤다는 뜻으로 해석.
기본 알고리즘 (TCP Reno 계열):
- Slow Start = cwnd 를 1 MSS 부터 시작, ACK 마다 2배씩 (지수 증가). 빠르게 대역 탐색.
- Congestion Avoidance = ssthresh (임계값) 넘으면 1 MSS 씩 (선형 증가). 조심스럽게.
- Fast Retransmit = dup ACK 3회 = 손실로 판단, timeout 안 기다리고 즉시 재전송.
- Fast Recovery = 손실 시 cwnd 절반으로 줄이고 선형 증가 재개 (timeout 시엔 1 로 리셋).
현대 알고리즘:
CUBIC= 리눅스 기본. 고대역·고 latency (BDP 큰) 환경에 최적화. window 증가가 3차 함수.BBR= 구글. 손실 대신 대역폭·RTT 측정 기반. bufferbloat 회피.
→ 혼잡 제어 = 네트워크 capacity 에 맞춤. 손실을 신호로 송신 속도 조절.
정리 — 송신측은 두 window 의 최솟값으로 보낸다:
| 제어 | 보호 대상 | 신호 | window |
|---|---|---|---|
| 흐름 제어 | 수신측 buffer | 수신측의 rwnd 통보 | receive window (rwnd) |
| 혼잡 제어 | 중간 네트워크 | 패킷 손실·RTT | congestion window (cwnd) |
대표적인 TCP
| 프로토콜 | 포트 | 주요 기능 | 사용 예시 |
|---|---|---|---|
| HTTP (HyperText Transfer Protocol) | 80 (HTTP), 443 (HTTPS) | 웹 페이지 요청/응답 | 웹브라우저로 웹사이트 보기 |
| FTP (File Transfer Protocol) | 21 (제어), 20 (데이터) | 파일 전송 (평문) | 옛날 웹 서버에 파일 업로드 |
| SFTP (SSH File Transfer Protocol) | 22 | 보안 파일 전송 | 서버에 로그/파일 업로드 |
| SMTP (Simple Mail Transfer Protocol) | 25, 587, 465 | 이메일 발송 | Gmail이 메일 보내는 방식 |
| LDAP (Lightweight Directory Access Protocol) | 389 (LDAP), 636 (LDAPS) | 사용자 인증, 디렉터리 조회 | 회사 사내 로그인 시스템, SSO |
TCP 커넥션의 본질
TCP 커넥션 = 물리적 연결이 아니라 양쪽 OS 커널 메모리에 들고 있는 상태(state) 다. 핸드셰이크가 “establish” 하는 건 케이블이 아니라 양쪽 커널의 자료구조이고, 핸드셰이크 전후로 물리 경로(케이블·라우터·스위치) 는 동일하다.
커널이 커넥션마다 들고 있는 것
커넥션이 맺어지면 양쪽 커널이 TCP control block · socket structure 를 메모리에 만든다.
- 4-tuple
(src IP, src port, dst IP, dst port)= 커넥션의 고유 신원 - 시퀀스 번호 = 어디까지 보냈고 받았는지
- 윈도우 크기 = 상대가 받을 수 있는 양
- 송수신 버퍼, 타이머
“커넥션이 ESTABLISHED” = 이 자료구조가 양쪽에 만들어졌고 두 쪽 다 ESTABLISHED 상태라는 뜻.
3-way handshake 가 실제로 하는 일
물리적 연결이 아니라 초기 시퀀스 번호 교환·합의다.
| 단계 | 방향 | 의미 |
|---|---|---|
SYN | A → B | ”대화 시작. 내 시작 시퀀스 번호 X” |
SYN-ACK | B → A | ”X 받음. 내 시작 시퀀스 번호 Y” |
ACK | A → B | ”Y 받음” |
왜 3 번인가 = 양쪽이 각자의 시퀀스 번호를 보내고 + 도달 확인까지 받아야 한다. A 번호는 1·2 번에서 확인, B 번호는 2·3 번에서 확인 → 양방향 확인에 최소 3 번.
UDP 와의 본질적 차이
TCP 의 “연결” = 신뢰성(순서 보장·손실 감지·재전송) 을 위한 공유 상태다. UDP 는 이 상태가 없어 “연결” 개념 자체가 없고 핸드셰이크도 없다.
사고실험 — 한쪽이 상태를 잃으면
A 와 B 가 커넥션 맺은 뒤 B 가 리부팅 → B 의 상태는 사라지지만 A 는 모른다.
- A 는 여전히 혼자
ESTABLISHED. - A 가 다음 데이터를 보낼 때 B 가
RST로 응답해야 비로소 끊긴 걸 안다.
귀결 = 연결은 양쪽의 상태이고, 한쪽이 상태를 잃어도 다른 쪽은 즉시 알 수 없다.
DB 커넥션과의 관계
DB 커넥션 = TCP 커넥션 위에 얹힌 상위 층.
- 수립 순서
- TCP 커넥션 수립 (3-way handshake). DB 는 정해진 포트에서 listen — MySQL
3306, PostgreSQL5432. - 그 위에서 DB 프로토콜 핸드셰이크 — 인증, DB 선택, 문자셋·세션 파라미터 협상.
- TCP 커넥션 수립 (3-way handshake). DB 는 정해진 포트에서 listen — MySQL
- “DB 커넥션 수립” = TCP 커넥션 + DB 세션 상태.
- DB 커넥션이 TCP 커넥션보다 무겁다 → 매 쿼리마다 새로 맺으면 비효율 → 커넥션 풀(HikariCP 등) 로 재사용.
송·수신 흐름과 encapsulation
TCP 데이터가 어떻게 네트워크를 타고 가나 — 각 계층이 자기 헤더를 차곡차곡 쌓는 게 핵심.
데이터 단위 (각 계층의 PDU)
| 단위 | 어느 계층 | 헤더 내용 |
|---|---|---|
| Segment | TCP (L4) | src/dst port, seq·ack 번호, window, flags |
| Packet (Datagram) | IP (L3) | src/dst IP, TTL, protocol |
| Frame | Ethernet (L2) | src/dst MAC, EtherType, FCS (checksum) |
Encapsulation — 송신 시
각 계층이 위에서 받은 데이터에 자기 헤더 부여:
[application data]
└─ TCP 부여 → [TCP hdr | data] = Segment
└─ IP 부여 → [IP hdr | TCP hdr | data] = Packet
└─ Eth → [Eth hdr | IP hdr | TCP hdr | data | FCS] = Frame
흐름:
- Application = 데이터를 socket 에
write(). - TCP = 데이터를 MSS 단위로 split, TCP 헤더(seq·ack·window·port) 부여 → Segment.
- IP = Segment 에 IP 헤더(src·dst IP, TTL) 부여 → Packet. 필요 시 fragmentation (MTU 보다 크면 쪼갬).
- Ethernet (NIC) = Packet 에 Ethernet 헤더(src·dst MAC, EtherType) + 끝에 FCS 부여 → Frame. 전기 신호로 송출.
Decapsulation — 수신 시
역순. 각 계층이 자기 헤더 검증·제거하고 위로 전달.
[Eth hdr | IP hdr | TCP hdr | data | FCS]
↓ Eth hdr·FCS 검증·제거
[IP hdr | TCP hdr | data]
↓ IP hdr 검증·제거
[TCP hdr | data]
↓ TCP hdr 검증·시퀀스 정렬·제거
[data]
↓ application 이 read 로 가져감
MSS 와 MTU
둘 다 “한 번에 보낼 수 있는 최대 크기” 인데, 어느 계층의 무엇에 대한 제한인지가 다르다.
| 항목 | 풀네임 | 계층 | 무엇의 최대 | 표준값 |
|---|---|---|---|---|
| MTU | Maximum Transmission Unit | L2 (Ethernet) | frame 의 payload (= IP 패킷 통째) | 이더넷 1500 |
| MSS | Maximum Segment Size | L4 (TCP) | TCP segment 의 data 부분 (헤더 제외) | 1460 |
관계 = MSS = MTU − IP 헤더(20) − TCP 헤더(20). 표준 이더넷·옵션 없는 케이스 = 1500 − 20 − 20 = 1460.
[Eth hdr | IP hdr (20) | TCP hdr (20) | data | FCS]
└─────── MTU 안에 들어가야 함 (1500) ──────┘
└─── MSS ───┘ (= MTU − 20 − 20 = 1460)
누가 강제하나
- MTU = L2 매체·NIC·드라이버. 이걸 넘으면 IP 가 packet 을 쪼개야 (= IP fragmentation, 성능 저하·loss 위험).
- MSS = TCP 가 사전에 fragmentation 안 일어나게 segment 크기를 미리 줄이는 장치. 3-way handshake 의 SYN 옵션으로 양쪽이 자기 MSS 광고 → 작은 쪽 채택.
왜 둘 다 필요한가
- MTU 만 있으면 TCP 가 “이 매체에서 안전한 데이터 크기” 를 모름 → 매번 큰 segment 만들고 IP 가 쪼개야 함.
- MSS 가 있어 TCP 가 application 데이터를 처음부터 안전 크기로 split → fragmentation 회피.
특수 케이스
- Jumbo frame = MTU
9000. MSS =9000 − 40 = 8960. 데이터센터 내부 throughput 용. - VPN·터널 = VPN 헤더(예: WireGuard ~60B) 추가로 실효 MTU 감소 → MSS 도 줄여야. 자동 처리 =
iptables ... --clamp-mss-to-pmtu. - IPv6 = IP 헤더 40B → MSS =
MTU − 40 − 20 = 1440. - PMTUD (Path MTU Discovery) = 경로 중 가장 작은 MTU 를 찾아 그에 맞춰 조정. ICMP “Fragmentation Needed” 메시지 사용.
MSS vs TCP buffer vs window
MSS·TCP buffer·window 셋 다 “TCP 크기” 와 관련이지만 의미가 완전히 다르다. 흔한 혼동.
| 항목 | 의미 | 크기 | 어디서 정해짐 |
|---|---|---|---|
| MSS | 한 TCP segment 의 data 최대 | ~1460 B | TCP 3-way handshake 합의 |
| TCP buffer | 소켓별 송·수신 큐 | 16K~수MB | 커널 sysctl net.ipv4.tcp_{r,w}mem |
| Window size | 상대가 한 번에 받을 수 있는 byte (in flight) | 동적 | TCP flow control (헤더의 window 필드) |
비유
- TCP buffer = 창고 (전체 보관량, 큼)
- MSS = 트럭 한 대 적재 한도 (한 번 운반 단위, 작음)
- window = 상대가 “지금 더 받을 수 있어요” 통보하는 양
송신 시 셋 다 어떻게 같이 동작
- application
write()→ 데이터가 TCP send buffer 에 들어감. - TCP 가 buffer 에서 꺼내 MSS 단위로 잘라 segment 만듦.
- 한 번에 보낼 수 있는 양은 상대의 receive window 까지 (window 가 작으면 buffer 가득 차도 못 보냄).
- ACK 받기 전까지 buffer 에서 안 지움 (재전송 대비).
확인 도구 — 자세한 건 07. Linux Networking > 패킷 버퍼 — 커널 vs 유저 공간 참고.
- buffer 현재량 =
ss -tm의Recv-Q·Send-Q - buffer 크기 한도 =
sysctl net.ipv4.tcp_rmem(수신),tcp_wmem(송신) - MSS 협상 결과 + window =
ss -ti의mss:1460,snd_wnd
흔한 혼동
- “TCP buffer 가 1460?” → 아니. buffer 는 훨씬 큼 (수십 KB~MB). MSS 는 segment 단위 한도.
- “MSS 크게 하면 buffer 도 커야?” → 아니. 독립 노브. MSS 는 L4·매체 관련, buffer 는 커널 메모리 관리.
- “Window = buffer?” → 부분 관계. receive window 가 receive buffer 의 남은 공간을 광고하는 식. buffer = 내 쪽 메모리, window = 상대에게 알리는 “더 받을 수 있어요” 양.
OSI / TCP-IP 계층 매핑
| OSI 7 | TCP/IP 4 | 데이터 단위 | 예시 |
|---|---|---|---|
| L7 Application | Application | data | HTTP, SSH, DNS |
| L6 Presentation | (Application) | data | TLS (일부 견해), 인코딩 |
| L5 Session | (Application) | data | Socket API, RPC |
| L4 Transport | Transport | Segment | TCP, UDP |
| L3 Network | Internet | Packet | IP, ICMP, ARP |
| L2 Data Link | Link | Frame | Ethernet, MAC, NIC driver |
| L1 Physical | Link | bit | 케이블·전기 신호, NIC PHY |
- Segment / Packet / Frame 은 헷갈리기 쉬움 — 계층마다 다른 단위 이름.
- TCP/IP 4 layer 가 실무에 가까움 (Session·Presentation·Application 묶음).
리눅스 커널 구현 측면
위는 프로토콜 보편 흐름. 실제 리눅스 커널이 이걸 어떻게 구현하나 — user/kernel mode 경계, socket buffer, syscall 복사, driver/NIC 까지 — 는 07. Linux Networking > TCP 송·수신 — 커널 구현 관점 참고.
대표적인 TCP 장애 유형
- AWS 같은 가상환경처럼 네트워크 인프라 환경이 좋은 상황에서는 비교적 발생 빈도가 낮은 장애 유형
- Packet Loss
- 패킷이 유실된 경우
- 보안 정책 때문에 패킷 필터링에서 걸러지는 상황이 다수
- 패킷을 못받은 호스트 입장에선 여전히 장애
- TCP Out of order
- 패킷의 순서가 뒤바뀐 경우
- Retransmission 과 Dup ACK
- 네트워크 혼잡 상황에 돌입
- 위 2가지 상황 때문에 따라오는 상황
- Packet Loss
- 명백하게 응용 프로그램의 논리적 오류일 가능성이 매우 높은 유형
- Zero-Window
- 수신측 버퍼에 여유 공간이 하나도 없는 경우
- 즉, TCP buffer 에서 패킷을 읽는 recv() 와 처리 간 시차 때문에 TCP buffer 가 가득 차는 경우 발생
- 패킷을 읽는 recv() 를 실행하는 스레드와 패킷 처리를 위한 스레드는 반드시 분리해야한다
- Zero-Window
SACK
SACK, Selective Acknowledgement 란 수신측 OS 가 Sliding Window 에서 대기중이던 패킷이 일부 로스됐을 때 로스된 패킷만 재수신하겠다는 ACK 이다. 즉, 기본 ACK 는 로스된 패킷부터 마지막 패킷까지 재수신을 요청하지만 SACK 은 로스된 패킷만 재수신한다.
Sliding Window
패킷을 watch 하는 window 로 패킷 수신양에 따라 크기가 변한다.
Window-Size
수신측 TCP buffer 의 여유공간이다. 즉, Zero-Window 는 Window-Size 가 0 일 때 발생한다.
UDP 란?
- UDP 는 데이터를 데이터그램 단위로 전송하는 프로토콜이다.
- IP 와 거의 같지만, PORT 와 체크섬 정도만 추가된 상태다.
- UDP 는 따로 신뢰성을 보장하기 위한 절차가 없어 속도가 빠르다.
- UDP 는 스트리밍 RTP 와 같은 연속성이 중요한 서비스에 사용된다.
- UDP 도 신뢰성을 UDP 자체에서 보장하지 않는 것 뿐이지 개발자가 직접 신뢰성을 보장하게끔 할 수 있다.
- HTTP/3 는 QUIC 이라는 프로토콜을 기반으로 하는데 이는 UDP 를 기반으로 한다.
- 즉, UDP 자체는 신뢰성을 보장하지 않지만 추가적인 정의를 통해 신뢰성을 보장받을 수 있다.