1. TCP에 대해서
TCP 3-way / 4-way Handshake에 대해서 설명하기 위해, 기본적으로 TCP에 대해서 알아보자.

TCP/UDP는 전송 계층에서 사용하는 데이터 전송 기법(프로토콜)이다.
(TCP/IP의 4계층의 관점에서 주로 이야기한다)
여기서 TCP가 신뢰도 보장에 사용하는 기법이 3-way, 4-way handShake이다.
3-way는 연결을 시작할 때, 4-way는 연결을 끊을 때 사용된다.
오늘의 학습 포인트
- TCP 3-way handshake를 설명하고, 왜 2-way가 아닌지 말해보기
- 4-way handshake에서 TIME_WAIT이 왜 필요한지 말해보기
2. TCP 연결 성립 과정
- TCP는 신뢰성을 확보할 때 3-way handshake라는 작업을 진행한다.
- 클라이언트와 서버가 통신할 때 세 단계의 과정을 거치는 것이다.

-
- "나 지금 연결할 준비됐어." — SYN
- "나도 준비됐어, 네가 준비됐다는 거 확인했어." — SYN-ACK
- "나도 네가 준비됐다는 거 확인했어." — ACK
- ⇒ 이렇게 신뢰성을 확보한 후 데이터 전송을 시작한다.

(1) ISN - 초기 시퀀스 번호
- 시퀀스 번호(ISN)는 데이터의 순서와 수신 확인에 사용되는 핵심 값이다.
- TCP 연결을 수립하려면 클라이언트와 서버가 서로의 초기 시퀀스 번호(ISN, Initial Sequence Number)를 알아야 한다.
- 이 ISN은 임의로 생성되는 값이다.
(2) 연결 수립의 3단계

Step 1 — SYN: 클라이언트가 연결을 요청합니다
- SYN 플래그 = 1 (연결 요청 표시)
- Seq = 100 (클라이언트 ISN, 보안을 위해 랜덤 생성)
- ISN이 랜덤인 이유: TCP Session Hijacking 방지
- 클라이언트 상태: LISTEN → SYN_SENT

Step 2 — SYN-ACK: 서버가 요청을 수락하고 자신의 ISN을 제안합니다
- SYN = 1 (서버도 자신의 ISN 제안)
- ACK = 1 (클라이언트 SYN 수신 확인)
- Seq = 200 (서버의 ISN)
- Ack = 101 (Seq=100 받았음, 다음은 101 보내라)
- 서버 상태: LISTEN → SYN_RECEIVED

Step 3 — ACK: 클라이언트가 서버의 ISN을 확인합니다. 연결 수립 완료!
- ACK = 1 (서버 SYN-ACK 수신 확인)
- Seq = 101 (클라이언트 다음 전송 번호)
- Ack = 201 (서버 Seq=200 받았음, 다음은 201 보내라)
- 양측 상태: → ESTABLISHED
- 이제 데이터 전송이 가능합니다
(3) 왜 2-way가 아닌 3-way인가?
이 질문은 단순히 "규칙이 그래서"가 아니라, 무엇이 잘못될 수 있는지를 설명할 수 있어야 한다.
2-way로 하면 SYN-ACK가 유실됐을 때 서버는 "연결이 수립됐다"고 판단하지만
클라이언트는 모르는 half-open 연결이 발생한다. 세 번째 ACK가 이 문제를 해결한다.

SYN-ACK를 보낸 서버는 클라이언트와 연결되었다고 착각하고,
유실된 SYN-ACK는 클라이언트에 도달하지 못한다.
응답을 받지 못한 클라이언트는 여전히 연결되지 않았다고 판단한다.
→ half-open 발생 (서버만 연결되었다고 믿고 기다리는 상황)
서버는 데이터가 오길 기다리며 불필요한 자원을 낭비하게 된다.

세 번째 ACK로 서버가 클라이언트의 수신을 확인한다.
half-open 연결이 발생하지 않는다.
3-way가 필요한 이유 요약
3. TCP 연결 해제 과정
(1) 4-way handshake

통신이 끝나고 TCP 연결 해제 시, 4-way handshake 과정이 발생한다.
전이중 통신
TCP는 전이중(Full-Duplex) 통신이다.
클라이언트→서버, 서버→클라이언트 두 방향의 채널이 독립적으로 존재한다.
따라서 연결을 끊을 때도 각 방향을 따로 닫아야 한다.
3way가 아닌 4way인 이유
연결 수립 시에는 서버가 SYN과 ACK를 하나의 패킷으로 합쳐 보낼 수 있었지만, 종료 시에는 불가능하다.
클라이언트가 종료(FIN)를 선언하더라도 서버는 아직 전송 중인 데이터가 남아 있을 수 있기 때문이다.

Step 1 — FIN: 클라이언트가 연결 종료를 요청합니다
- FIN 플래그 = 1 (더 이상 보낼 데이터 없음)
- 클라이언트 상태: ESTABLISHED → FIN_WAIT_1
- 클라이언트→서버 방향만 닫힘 (Half-Close)
- 클라이언트는 여전히 서버 데이터를 수신할 수 있음

Step 2 — ACK: 서버가 FIN 수신을 확인합니다. 아직 데이터가 남을 수 있습니다
- ACK = 1, Ack = 301 (FIN 수신 확인)
- 서버 상태: ESTABLISHED → CLOSE_WAIT
- 클라이언트 상태: FIN_WAIT_1 → FIN_WAIT_2
- 서버는 아직 보낼 데이터가 있을 수 있음 (Half-Close 유지)
- 이것이 4-way인 이유: ACK와 FIN을 즉시 합칠 수 없음

Step 3 — FIN: 서버가 모든 데이터 전송 완료 후 종료를 선언합니다
- FIN 플래그 = 1
- 서버 상태: CLOSE_WAIT → LAST_ACK
- 서버→클라이언트 방향도 닫힘 예정
- 마지막 ACK를 기다리는 상태

Step 4 — ACK: 클라이언트가 서버 FIN을 확인. TIME_WAIT 시작!
- ACK = 1, Ack = 501 (서버 FIN 확인)
- 서버 상태: LAST_ACK → CLOSED
- 클라이언트 상태: FIN_WAIT_2 → TIME_WAIT
- TIME_WAIT: 2*MSL(약 120초) 대기 후 CLOSED
- 마지막 ACK 유실 대비 + 이전 패킷 혼동 방지
(2) TIME_WAIT
- TIME_WAIT
- 마지막 ACK를 보낸 후 클라이언트는 즉시 포트를 닫지 않는 이유는 무엇일까?
- 첫째, 마지막 ACK가 유실될 경우 서버의 재전송 FIN을 처리하기 위해서입니다.
- 둘째, 이전 연결의 지연 패킷이 새 연결에 끼어드는 것을 방지하기 위해서입니다.
- 마지막 ACK를 보낸 후 클라이언트는 즉시 포트를 닫지 않는 이유는 무엇일까?
마지막 ACK 유실 대비

TCP 연결을 종료할 때, 마지막 ACK(A가 B에게 보내는 확인 응답)가 네트워크상에서 사라질 수 있습니다.
- 상황:
- A가 마지막 ACK를 보내고 즉시 연결을 닫아버렸는데,
이 ACK가 유실되었다고 가정해 보자.
- A가 마지막 ACK를 보내고 즉시 연결을 닫아버렸는데,
- 상태:
- 상대방(B)은 본인이 보낸 FIN에 대한 응답을 받지 못했으므로, 다시 FIN을 재전송한다.
- TIME_WAIT의 역할:
- 이때 A가 TIME_WAIT 상태로 남아있지 않고 이미 소켓을 닫았다면,
재전송된 FIN에 대해RST(오류) 응답을 보내게 된다. (정상적인 종료X)
- 이때 A가 TIME_WAIT 상태로 남아있지 않고 이미 소켓을 닫았다면,
- 해결:
- A가 TIME_WAIT 상태로 기다리고 있으면,
다시 날아온 FIN에 대해 ACK를 다시 보내줄 수 있어 상대방이 연결을 종료할 수 있게 됩니다.
- A가 TIME_WAIT 상태로 기다리고 있으면,

지연 패킷 차단

네트워크 경로 문제로 인해, 이전 연결에서 보낸 데이터 패킷이 아주 늦게 도착하는 경우가 있다.
- 위험성:
- 만약 연결 종료 직후 같은 포트 번호로 새로운 연결이 생겼는데,
뒤늦게 도착한 옛날 패킷(지연 패킷)이 새 연결의 데이터인 것처럼 끼어들면 데이터 오염이 발생한다.
- 만약 연결 종료 직후 같은 포트 번호로 새로운 연결이 생겼는데,
- TIME_WAIT의 역할:
- TCP는 보통 2MSL(Maximum Segment Lifetime의 2배, 약 1~4분) 동안 해당 포트를 사용하지 못하게 묶어둔다.
질문 대비
Q: TIME_WAIT이 너무 많아지면 어떤 문제가 생길까요?
- A: 서버 입장에서 TIME_WAIT 상태의 소켓이 너무 많아지면, 새로운 연결을 수립할 포트가 고갈될 수 있습니다. 그래서 실무에서는 이 시간을 튜닝하거나 포트를 재사용하는 설정을 쓰기도 합니다.
4. 비정상 종료 - RST 패킷
(1) RST 패킷이란?
- 연결을 즉각 종료하는 강제 종료 신호이다.
- FIN을 통한 정상 종료와 달리,
버퍼에 남은 데이터를 버리고 양측 모두 즉시 CLOSED 상태로 전환한다. - TIME_WAIT 없이 즉각 종료되므로 포트 고갈 문제를 피할 수 있지만,
데이터 손실 위험이 있어 정상적인 상황에서는 사용하지 않는다.
(2) 언제 발생하나?

- 포트가 닫혀 있을 때: 클라이언트가 서버의 8080 포트로 연결을 시도했는데,
서버에서 그 포트를 안 쓰고 있다면 서버가 RST를 보낸다. - 비정상적인 상태: 이미 연결이 끊긴 줄 모르고 데이터를 보냈을 때, 받는 쪽에서 RST를 날린다.
- 보안 장비의 개입: 방화벽이나 침입 차단 시스템(IPS)에서 유해한 통신이라고 판단하면, 양쪽에 RST를 보내서 강제로 연결을 끊어버린다.
5. 질문 답변
- TCP 3-way handshake를 설명하고, 왜 2-way가 아닌지 말씀해주세요.
TCP는 데이터 전송 전 반드시 3-way handshake(SYN → SYN-ACK → ACK)를 수행합니다. 따라서 클라이언트가 서버의 응답을 잘 받았다는 세 번째 ACK를 보내줌으로써, 양방향 모두 통신 준비가 되었음을 확정하고 자원 낭비를 막는 것입니다.
2-way로는 충분하지 않은 이유는 half-open 연결 때문입니다. 2-way로 진행할 경우, 서버가 클라이언트의 요청에 응답(SYN-ACK)한 것만으로 서버는 연결이 되었다고 판단하지만, 만약 이 응답이 유실되면 클라이언트는 연결이 안 된 것으로 판단하는 Half-Open 상태가 됩니다. TCP 3-way handshake는 통신 전 양측의 상태를 확인하고 초기 시퀀스 번호(ISN)를 동기화하여 신뢰성 있는 연결을 맺는 과정입니다. - 4-way handshake에서 TIME_WAIT이 왜 필요한지 말씀해주세요.
TIME_WAIT은 연결 해제의 안전장치 역할을 하며, 크게 두 가지 이유로 필수적입니다.
첫째, 마지막 ACK 유실에 대비하기 위해서입니다. 클라이언트가 보낸 마지막 ACK가 사라지면 서버는 FIN을 재전송하게 됩니다. 이때 클라이언트가 이미 소켓을 닫았다면 서버는 정상 종료를 못 하고 오류(RST)를 받게 됩니다. 반면 TIME_WAIT 상태가 있다면 재전송된 FIN에 다시 ACK를 보내주어 정상적인 종료를 보장할 수 있습니다.
둘째, 지연 패킷으로 인한 데이터 오염 방지입니다. 이전 연결에서 네트워크를 떠돌던 패킷이 나중에 생성된 동일한 포트의 새 연결에 도착할 경우, 데이터가 섞이는 문제가 발생할 수 있습니다. 이를 방지하기 위해 패킷의 최대 수명의 2배 동안 대기하여 잔여 패킷이 완전히 소멸되도록 기다리는 것입니다. - 연결 해제 시 3-way handshake가 아닌, 4-way handshake인 이유에 대해서 말씀해주세요.
연결 시에는 서버가 클라이언트의 요청을 확인(ACK)함과 동시에 자신의 통신 준비(SYN)를 마칠 수 있어 하나의 패킷(SYN-ACK)으로 합칠 수 있습니다. 하지만 종료 시에는 클라이언트가 종료를 선언(FIN)하더라도, 서버는 아직 보낼 데이터가 남아있을 수 있습니다. 따라서 서버는 우선 수신 확인(ACK)만 보낸 뒤, 남은 데이터를 모두 전송한 후에 별도로 종료(FIN)를 선언해야 하므로 총 4단계가 필요합니다.
- 참고 자료: https://dev-letter.kr/