웹 브라우저에서 페이지를 요청하거나 파일을 전송할 때, 데이터를 보내기 전에 먼저 상대방과 '연결'을 맺는 과정이 있습니다. 이 연결을 수립하지 않으면, 데이터가 올바른 순서로 전달되었는지, 상대방이 실제로 수신 가능한 상태인지 확인할 방법이 없습니다.
TCP는 이 문제를 해결하기 위해 데이터 전송 전에 반드시 연결을 수립하고, 전송이 끝나면 연결을 명시적으로 해제합니다. 그렇다면 이 연결은 정확히 몇 단계로, 어떤 이유로 그 단계가 결정되는 걸까요?
이 글에서는 TCP 3-way Handshake로 연결이 수립되는 과정, 4-way Handshake로 연결이 해제되는 과정, 그리고 TIME_WAIT 상태가 왜 필요한지를 흐름 중심으로 정리해 보겠습니다.
요약
- 3-way Handshake: TCP 연결 수립 — SYN → SYN-ACK → ACK 3단계
- 4-way Handshake: TCP 연결 해제 — FIN → ACK → FIN → ACK 4단계
- 왜 3단계인가: 양쪽이 서로의 송수신 능력을 확인하는 최소 단계
- 왜 해제는 4단계인가: 서버가 FIN을 받은 시점에 아직 보낼 데이터가 남아 있을 수 있기 때문
- TIME_WAIT: 클라이언트가 마지막 ACK 전송 후 일정 시간 대기하는 상태
1. 왜 연결 수립이 필요한가
TCP는 UDP와 달리 데이터를 보내기 전에 먼저 연결을 수립합니다. 이 과정이 필요한 이유는 다음과 같습니다.
- 상대방이 존재하는지 확인: 서버가 실제로 요청을 받을 수 있는 상태인지 확인합니다.
- 시퀀스 번호 동기화: 양쪽이 패킷 순서를 관리하기 위한 초기 시퀀스 번호를 교환합니다.
- 송수신 능력 상호 확인: 클라이언트가 보낼 수 있고, 서버가 받을 수 있으며, 서버가 보낼 수 있고, 클라이언트가 받을 수 있음을 모두 확인합니다.
이 세 가지를 확인하는 최소 단계가 3단계입니다. 2단계로는 양방향 확인이 불가능하고, 4단계는 3단계의 SYN-ACK 하나로 처리할 수 있는 내용(수신 확인 + 연결 요청)을 별도 패킷 두 개로 나누는 것이므로 중복됩니다.
2. 3-way Handshake — 연결 수립

동작 순서
1. SYN 클라이언트 → 서버
"연결 요청합니다. 제 초기 시퀀스 번호는 x입니다."
2. SYN-ACK 서버 → 클라이언트
"수락합니다(ACK=x+1). 제 초기 시퀀스 번호는 y입니다."
3. ACK 클라이언트 → 서버
"확인했습니다(ACK=y+1). 연결 완료."
각 플래그의 의미
- SYN (Synchronize): 연결 요청 신호. 초기 시퀀스 번호(ISN, Initial Sequence Number)를 포함합니다.
- ACK (Acknowledgment): 수신 확인 신호. ACK 번호는 "다음에 받을 시퀀스 번호"를 의미합니다. SYN/FIN 패킷은 데이터가 없어도 시퀀스 번호를 1 소비하므로 ACK = 상대방 SEQ + 1입니다. 데이터 전송 중에는 ACK = 상대방 SEQ + 수신한 바이트 수로 증가합니다.
- SYN-ACK: SYN과 ACK를 동시에 보내는 것으로, 연결 수락과 자신의 연결 요청을 한 번에 처리합니다.
시퀀스 번호 교환의 의미
3-way handshake에서 단순히 연결 확인만 하는 것이 아니라 시퀀스 번호도 함께 교환합니다. 이 번호를 기반으로 이후 패킷의 순서를 관리하고, 손실된 패킷을 감지합니다.
주의: 초기 시퀀스 번호(ISN)는 고정값이 아니라 무작위로 생성됩니다. 예측 가능한 번호를 사용하면 TCP 시퀀스 번호 예측 공격에 취약해지기 때문입니다.
3. 3-way Handshake 이후 — 데이터 전송
연결이 수립되면 양방향으로 데이터를 주고받을 수 있습니다. 각 데이터 패킷은 시퀀스 번호와 함께 전송되고, 수신 측은 ACK로 확인 응답을 보냅니다.
# 아래는 동작 원리 설명을 위한 단순화 예시입니다.
# 실제 ACK 번호는 SEQ + 수신한 데이터 바이트 수만큼 증가하며,
# HTTP GET 요청처럼 수십~수백 바이트를 포함하는 경우 ACK = x+1+(요청 크기)가 됩니다.
클라이언트 → 서버: [SEQ=x+1] HTTP GET /
서버 → 클라이언트: [ACK=x+2] 수신 확인 # 단순화 (실제: x+1+요청바이트수)
서버 → 클라이언트: [SEQ=y+1] HTTP 200 OK (응답 데이터)
클라이언트 → 서버: [ACK=y+2] 수신 확인 # 단순화 (실제: y+1+응답바이트수)
이 과정에서 ACK가 일정 시간 안에 오지 않으면 재전송이 발생합니다. 재전송 타이머(RTO)의 계산 원리는 8편에서 자세히 다룹니다.
4. 4-way Handshake — 연결 해제

연결을 종료할 때는 3단계가 아닌 4단계가 필요합니다.
동작 순서
1. FIN 클라이언트 → 서버
"저는 보낼 데이터를 모두 보냈습니다. 연결을 종료하겠습니다."
2. ACK 서버 → 클라이언트
"수신 확인. 아직 전송 중인 데이터가 있어 FIN은 별도로 전송합니다."
(서버는 클라이언트의 FIN을 받은 시점에 아직 전송 중인 데이터가 있을 수 있습니다.
ACK로 수신을 확인하되, 데이터를 모두 보낸 뒤 별도로 FIN을 전송합니다.)
3. FIN 서버 → 클라이언트
"저도 보낼 데이터를 모두 보냈습니다. 연결을 종료하겠습니다."
4. ACK 클라이언트 → 서버
"확인했습니다. 연결 종료."
왜 해제는 4단계인가
연결 수립(3단계)과 달리 해제가 4단계인 이유는 Half-close 개념 때문입니다.
클라이언트가 FIN을 보내면 클라이언트→서버 방향의 연결만 끊깁니다. 이것을 Half-close라고 하며, 서버→클라이언트 방향은 아직 열려 있습니다. 서버는 위 2단계에서 설명한 것처럼 전송 중인 데이터가 있을 수 있으므로 ACK와 FIN을 즉시 합쳐 보내지 않고 분리합니다. ACK로 수신을 먼저 확인하고, 남은 데이터를 모두 보낸 뒤 FIN을 전송합니다. 이 때문에 3단계가 아닌 4단계가 됩니다.
5. TIME_WAIT 상태
클라이언트가 마지막 ACK를 보낸 뒤 즉시 연결을 닫지 않고 TIME_WAIT 상태로 일정 시간 대기합니다.
클라이언트가 마지막 ACK 전송
↓
TIME_WAIT 상태 진입 (기본값: 2×MSL)
(RFC 793 기준 MSL=120초 → 2×MSL=240초,Linux 실제 구현은 약 60초로 하드코딩)
↓
TIME_WAIT 종료 후 연결 완전 종료
TIME_WAIT가 필요한 이유 2가지
- 마지막 ACK 유실 대비: 클라이언트의 마지막 ACK가 유실되면 서버는 FIN을 재전송합니다. TIME_WAIT 상태에서 이 재전송된 FIN을 받아 다시 ACK를 보낼 수 있습니다. 연결을 즉시 닫아버리면 재전송된 FIN에 응답할 수 없습니다.
- 지연 패킷 처리: 네트워크에 남아 있는 이전 연결의 지연 패킷이 새 연결에 혼입되는 것을 방지합니다.
주의: 서버에서 짧은 시간에 많은 연결을 처리하면 TIME_WAIT 상태의 소켓이 대량으로 쌓여 포트 고갈 문제가 발생할 수 있습니다. SO_REUSEADDR 소켓 옵션이나 tcp_tw_reuse 커널 파라미터로 완화할 수 있습니다.
6. 연결 상태 다이어그램
TCP 연결은 여러 상태를 거칩니다. 실무에서 netstat 명령어로 확인할 수 있습니다.
| 상태 | 설명 |
| LISTEN | 서버가 연결 요청을 기다리는 상태 |
| SYN_SENT | 클라이언트가 SYN을 보내고 응답 대기 |
| SYN_RECEIVED | 서버가 SYN을 받고 SYN-ACK를 보낸 상태 |
| ESTABLISHED | 연결 수립 완료, 데이터 전송 가능 |
| FIN_WAIT_1 | 클라이언트가 FIN을 보내고 ACK 대기 |
| FIN_WAIT_2 | ACK를 받고 서버의 FIN 대기 |
| CLOSE_WAIT | 서버가 FIN을 받고 ACK를 보낸 상태 |
| LAST_ACK | 서버가 FIN을 보내고 ACK 대기 |
| TIME_WAIT | 마지막 ACK 전송 후 대기 |
| CLOSED | 연결 완전 종료 |
아래 명령어로 현재 시스템의 TCP 연결 상태를 직접 확인할 수 있습니다.
# 현재 TCP 연결 상태 확인
netstat -an | grep ESTABLISHED
netstat -an | grep TIME_WAIT
추가 팁
SYN Flood 공격
공격자가 다수의 SYN 패킷을 보내고 ACK를 보내지 않으면, 서버는 SYN_RECEIVED 상태의 연결을 대량으로 유지하게 됩니다. 서버의 연결 대기 큐(backlog)가 가득 차면 정상적인 연결 요청을 처리하지 못하는 서비스 거부(DoS) 상태가 됩니다. 이를 방어하기 위해 SYN Cookie 기법을 사용합니다.
Keep-Alive
HTTP/1.1은 기본적으로 Keep-Alive를 사용합니다. 하나의 TCP 연결을 유지하면서 여러 HTTP 요청을 처리합니다. 매번 3-way handshake를 반복하는 오버헤드를 줄일 수 있습니다.
TCP Fast Open (TFO)
3-way handshake의 지연을 줄이기 위해 첫 번째 SYN 패킷에 데이터를 포함시키는 기법입니다. 이전에 연결한 적 있는 서버와 통신 시 RTT를 1회 줄일 수 있습니다(RFC 7413).
📌 정리
- 3-way Handshake는 SYN → SYN-ACK → ACK 3단계로 연결을 수립합니다.
- 4-way Handshake는 FIN → ACK → FIN → ACK 4단계로 연결을 해제합니다.
- 해제가 4단계인 이유는 서버가 FIN 수신 시점에 아직 보낼 데이터가 남아 있을 수 있기 때문입니다.
- TIME_WAIT는 마지막 ACK 유실 대비와 지연 패킷 혼입 방지를 위해 필요합니다.
📌 실무에서 중요한 이유
- netstat으로 TIME_WAIT 상태 소켓이 대량으로 쌓여 있다면 포트 고갈을 의심합니다.
- SYN Flood 공격 방어를 위해 SYN Cookie 설정 여부를 확인합니다.
- HTTP Keep-Alive 설정은 TCP 연결 수립 오버헤드를 줄이는 데 중요합니다. 특히 지연이 큰 환경에서 효과가 큽니다.
- 로드밸런서 앞단에서 클라이언트 연결이 자주 끊기는 문제가 발생하면 TCP Handshake 과정의 타임아웃 설정을 확인합니다.
참고 자료
- RFC 793 - Transmission Control Protocol
- RFC 7413 - TCP Fast Open
- RFC 6298 - Computing TCP's Retransmission Timer
'cs 기초 > 네트워크' 카테고리의 다른 글
| [네트워크] TCP 속도는 왜 느려질까? (흐름 제어와 혼잡 제어) (0) | 2026.05.18 |
|---|---|
| [네트워크] 패킷이 손실되면 TCP는 어떻게 복구할까? (재전송, RTO) (0) | 2026.05.15 |
| [네트워크] IP 주소와 MAC 주소: 왜 두 가지 주소가 필요한가? (0) | 2026.04.27 |
| [네트워크] TCP와 UDP: 어떻게 다르고, 언제 선택해야 하는가? (0) | 2024.06.12 |
| [네트워크] ARP 프로토콜: 어떻게 동작하고, 왜 위험한가? (0) | 2024.06.12 |