TCP `4-way handshaking` 완전 정복 — `FIN`, `ACK`, `TIME_WAIT`까지

스터디·8분 읽기

4-way handshaking, 왜 따로 알아야 하나요?

TCP vs UDP 글까지 읽고 나면 이런 질문이 남습니다.

  • 3-way handshake는 많이 들어봤는데, 4-way handshaking은 정확히 무엇일까요?
  • 왜 연결을 닫는 데는 SYN, ACK처럼 세 번이 아니라 네 번이 필요할까요?
  • 서버에 CLOSE_WAIT가 많이 쌓였다는 말은 무슨 뜻일까요?
  • TIME_WAIT는 왜 마지막에 꼭 남고, 언제 문제처럼 보일까요?
  • FIN으로 닫는 것과 RST로 끊는 것은 무엇이 다를까요?

핵심은 이것입니다.

TCP 연결 종료는 "한 번에 끊는 동작"이 아니라, 양방향 스트림을 각각 독립적으로 닫아 가는 과정입니다

즉:

  • TCP는 양방향 통신이고
  • 내가 보내는 방향과 상대가 보내는 방향은 따로 닫을 수 있으며
  • 그래서 연결 종료도 보통 FINACK가 나뉘어 오가게 됩니다

이 글에서는 4-way handshaking을 단순 암기 절차로 보지 않고, FINACK가 네 번 오가는지, half-close, TIME_WAIT, CLOSE_WAIT, RST와 어떤 관계가 있는지 를 기준으로 정리하겠습니다.

기준: 이 글은 TCP 연결 종료 동작을 RFC 9293 기준으로 설명합니다. 표현상 4-way handshaking이라고 부르지만, RFC는 보통 normal close sequence using a FIN handshake 라고 설명합니다.

먼저 가장 짧은 답부터 보면

실무에서는 아래 정도만 먼저 잡아도 큰 틀은 흔들리지 않습니다.

질문 짧은 답
4-way handshaking이란? TCP 연결을 정상 종료할 때 FIN/ACK를 교환하는 대표 흐름
왜 네 번인가? 양방향 스트림을 각각 닫아야 해서 ACK와 반대편 FIN이 분리될 수 있기 때문
누가 TIME_WAIT에 들어가나? 보통 active close 를 시작한 쪽
CLOSE_WAIT는 왜 쌓이나? 원격 FIN은 받았지만 로컬 애플리케이션이 자기 쪽 close를 안 했기 때문
RST와 차이는? FIN은 정상 종료, RST는 연결을 즉시 중단하는 abort에 가깝습니다

가장 짧게 줄이면 이렇습니다.

  • 4-way handshakingTCP의 정상 종료 절차입니다
  • 핵심은 "양쪽이 각자 보내던 방향을 따로 닫는다"는 점입니다
  • 그래서 TIME_WAIT, CLOSE_WAIT 같은 상태도 종료 과정의 일부로 이해해야 합니다

Phase 1. 왜 연결 종료는 시작보다 더 복잡해 보일까?

3-way handshake는 연결을 시작하는 절차입니다. 이때는 양쪽이 "이제 연결을 만들자"는 데 동의하면 됩니다.

반면 종료는 조금 다릅니다.

연결 종료는 "앞으로 더 보낼 데이터가 없다"는 사실을 각 방향마다 따로 합의해야 합니다

즉, TCP에서는:

  • 내가 보내는 스트림
  • 상대가 보내는 스트림

이 독립적으로 닫힐 수 있습니다.

그래서 한쪽이 먼저:

나는 더 이상 보낼 데이터가 없습니다

라고 말해도, 상대는 아직:

나는 보낼 데이터가 남아 있습니다

라고 할 수 있습니다.

이 특성 때문에 종료는 보통 한 번에 끝나지 않고, 한 방향 종료 확인 + 반대 방향 종료 확인 으로 나뉩니다.

Phase 2. FINACK는 각각 무엇을 뜻할까?

4-way handshaking을 이해하려면 FINACK를 정확히 나눠 봐야 합니다.

FIN은 "더 보낼 데이터가 없다"는 뜻이다

FIN은 대충 "연결 끊자"가 아닙니다. 더 정확히는:

이 방향으로는 더 이상 보낼 사용자 데이터가 없습니다

라는 뜻입니다.

그래서 한쪽이 FIN을 보냈다고 해서, 연결 전체가 즉시 사라지는 것은 아닙니다.

상대는 여전히:

  • 지금까지 받은 데이터는 읽어야 하고
  • 자기가 남은 데이터를 보낼 수도 있습니다

ACK는 "네 FIN은 받았다"는 뜻이다

상대가 ACK를 보내면, 이는 보통:

네가 더 이상 안 보낸다는 뜻은 알겠다

는 의미입니다.

하지만 이것만으로 상대도 자기 송신을 끝냈다는 뜻은 아닙니다.

즉:

  • FIN = 나의 송신 종료 선언
  • ACK = 네 종료 선언 수신 확인

으로 보는 편이 정확합니다.

Phase 3. 4-way handshaking은 실제로 어떻게 흐를까?

가장 흔한 정상 종료 흐름은 아래처럼 그릴 수 있습니다.

Client -> Server : FIN
Client <- Server : ACK
Client <- Server : FIN
Client -> Server : ACK

이걸 단계별로 풀면 이렇습니다.

1. 먼저 닫고 싶은 쪽이 FIN을 보낸다

예를 들어 클라이언트가 먼저 요청/응답을 다 마쳤고 더 보낼 데이터가 없다고 가정해 보겠습니다.

그러면:

Client -> Server : FIN

을 보냅니다.

이 순간 클라이언트는 보통 FIN-WAIT-1 같은 상태로 들어가며, 자기 송신 방향을 닫기 시작한 상태가 됩니다.

2. 상대는 ACK로 받았다고 답한다

서버는 이 FIN을 받으면:

Client <- Server : ACK

를 보냅니다.

이 뜻은:

  • 클라이언트의 송신 종료 선언은 받았고
  • 하지만 서버 자신은 아직 보낼 것이 남아 있을 수 있다는 뜻

입니다.

이 시점이 바로 half-close를 이해하는 핵심입니다.

3. 상대가 자기 송신도 끝낼 때 FIN을 보낸다

서버 애플리케이션도 응답을 다 마쳤거나 더 보낼 데이터가 없어지면:

Client <- Server : FIN

을 보냅니다.

즉, 서버도 이제 자기 방향 송신을 닫습니다.

4. 마지막으로 ACK를 보내고 종료를 확정한다

클라이언트는 마지막으로:

Client -> Server : ACK

를 보내고, 정상 종료 시퀀스가 마무리됩니다.

다만 여기서 끝난 것처럼 보여도, active close를 시작한 쪽은 보통 바로 사라지지 않고 TIME_WAIT에 들어갑니다.

Phase 4. 왜 정확히 네 번이어야 할까?

여기서 자주 나오는 질문이 이것입니다.

3-way handshake는 세 번인데 종료는 네 번일까?

핵심은 시작과 종료의 대칭성이 완전히 같지 않기 때문입니다.

연결 시작에서는:

  • 서로 연결을 만들 준비가 되었는지
  • 초기 시퀀스 상태를 맞추는지

를 빠르게 합의하면 됩니다.

반면 연결 종료에서는:

  • 내 송신 종료
  • 네 송신 종료

가 분리될 수 있습니다.

그래서 상대는 내 FIN을 받자마자:

  • ACK로 먼저 응답하고
  • 자기 애플리케이션이 실제로 닫을 준비가 되었을 때 나중에 FIN을 보낼 수 있습니다

즉, ACK와 반대편 FIN이 논리적으로 분리될 수 있어서 네 단계처럼 보이는 것입니다.

꼭 항상 패킷 네 개가 보이느냐?

엄밀히 말하면, 캡처에서는 상황에 따라 ACKFIN이 같은 세그먼트에 함께 실려 보일 수도 있습니다.

예를 들어 RFC 9293의 normal close 예시에서도 FIN,ACK 형태가 등장합니다.

즉:

  • 흔히 설명할 때는 FIN -> ACK -> FIN -> ACK 네 단계로 이해하고
  • 실제 패킷 단위에서는 ACK가 다른 제어 비트와 합쳐질 수 있으며
  • 동시에 닫으면 CLOSING 같은 다른 상태 흐름도 나올 수 있습니다

그래서 4-way handshaking개념적 종료 절차로 이해하는 편이 정확합니다.

Phase 5. half-close는 정확히 무엇일까?

TCP 종료를 이해할 때 중요한 개념이 half-close입니다.

한쪽이 FIN을 보내면, 그쪽은:

  • 더 이상 보내지는 않지만
  • 상대가 보내는 데이터는 아직 받을 수 있습니다

즉, 연결 전체가 죽은 것이 아니라 한 방향만 먼저 닫힌 상태가 됩니다.

예를 들어:

  1. 클라이언트가 요청 바디를 다 보냄
  2. FIN 전송
  3. 서버는 그 요청을 다 읽고 처리
  4. 응답을 보낸 뒤 자기 FIN 전송

같은 흐름이 가능합니다.

즉, half-close는 이상한 예외가 아니라, TCP 종료가 양방향 독립이라는 사실의 자연스러운 결과입니다.

Phase 6. TIME_WAIT는 왜 남을까?

실무에서 가장 많이 보이는 종료 관련 상태는 TIME_WAIT입니다.

TIME_WAIT는 active close 쪽에서 주로 보인다

RFC 9293 기준으로 normal close sequence에서 먼저 닫기를 시작한 쪽은 마지막 ACK를 보낸 뒤 TIME_WAIT에 들어갑니다.

이 상태의 목적은 크게 두 가지입니다.

  1. 마지막 ACK가 상대에게 도달했는지 여유를 둔다
  2. 이전 연결의 지연 세그먼트가 새 연결과 섞이지 않게 한다

RFC 9293도 TIME-WAIT를, 원격 피어가 종료 요청에 대한 ACK를 받았는지 충분한 시간을 기다리고 지연 세그먼트의 영향을 피하기 위한 상태로 설명합니다.

그래서 왜 2MSL 이야기가 나올까?

보통 TIME_WAIT2MSL과 함께 설명됩니다.

  • MSL은 최대 세그먼트 생존 시간 개념이고
  • 2MSL은 패킷 왕복 지연과 지연 세그먼트 정리를 고려한 대기 시간입니다

즉, TIME_WAIT는 "쓸데없이 남는 상태"가 아니라, 정상 종료를 안전하게 마무리하기 위한 완충 구간입니다.

언제 문제처럼 보일까?

실무에서는:

  • 짧은 연결이 매우 많고
  • active close를 애플리케이션이 계속 하고
  • 포트 재사용 압박이 있는 환경

에서 TIME_WAIT가 많아 보일 수 있습니다.

하지만 상태가 많다는 사실 자체가 곧 버그는 아닙니다. 오히려 정상 종료가 잘 되고 있다는 흔적일 수도 있습니다.

Phase 7. CLOSE_WAIT는 왜 쌓일까?

이건 TIME_WAIT보다 더 자주 진짜 문제입니다.

CLOSE_WAIT는 원격 FIN은 받았는데, 로컬이 아직 안 닫았다는 뜻이다

상대가 FIN을 보내면 로컬 커널은 이를 ACK하고, 애플리케이션에게 연결이 닫히는 중이라고 알립니다.

이후 애플리케이션이 자기 쪽 close를 호출해야:

  • FIN을 보내고
  • LAST-ACK를 거쳐
  • 완전히 종료됩니다

그런데 애플리케이션이 이 종료를 미루거나 누락하면 CLOSE_WAIT가 오래 남습니다.

즉:

CLOSE_WAIT가 많이 쌓인다는 것은 대개 네트워크 문제보다 애플리케이션이 소켓을 제대로 닫지 않는 문제에 가깝습니다

이 때문에 서버 운영에서 CLOSE_WAIT는 진짜 점검 대상이 되는 경우가 많습니다.

Phase 8. FIN 종료와 RST 종료는 어떻게 다를까?

RFC 9293는 TCP 연결 종료를 크게 두 가지로 설명합니다.

  1. 정상적인 FIN 기반 종료
  2. RST를 보내고 상태를 즉시 버리는 abort

FIN은 정상 종료다

FIN 기반 종료는:

  • 남아 있는 데이터 순서를 보존하고
  • 상대에게 종료 사실을 예고하며
  • 정상적으로 정리할 기회를 줍니다

즉, graceful close에 가깝습니다.

RST는 즉시 중단에 가깝다

반면 RST는 보통:

  • 연결이 비정상 상태이거나
  • 더 이상 정상 종료 절차를 밟지 않고
  • 즉시 연결을 끊어야 할 때

사용됩니다.

즉, RST는:

  • "정상적으로 다 보냈으니 닫겠습니다"가 아니라
  • "이 연결은 더 이상 유효하지 않으니 즉시 버리겠습니다"

에 가깝습니다.

그래서 애플리케이션 관점에서도 FIN 종료와 RST 종료는 같은 일이 아닙니다.

핵심만 다시 정리하면

마지막으로 4-way handshaking을 한 번 더 정리하면 이렇게 볼 수 있습니다.

구분 의미
첫 번째 FIN 한쪽이 자기 송신 종료를 선언
첫 번째 ACK 상대가 그 종료 선언을 확인
두 번째 FIN 상대도 자기 송신 종료를 선언
마지막 ACK 종료 절차를 최종 확인
TIME_WAIT active close 쪽이 마지막 정리를 위해 대기
CLOSE_WAIT 원격은 닫았지만 로컬 애플리케이션이 아직 안 닫음

핵심 포인트는 다섯 가지입니다.

  1. 4-way handshakingTCP 연결을 정상 종료할 때 가장 대표적으로 나타나는 절차입니다.
  2. 종료가 네 단계처럼 보이는 이유는 양방향 스트림을 각각 독립적으로 닫기 때문입니다.
  3. 한쪽이 FIN을 보냈다고 해서 연결 전체가 즉시 끝나는 것은 아니며, 이 사이에 half-close가 가능합니다.
  4. TIME_WAIT는 정상 종료를 안전하게 마무리하기 위한 상태이고, CLOSE_WAIT는 대개 애플리케이션의 소켓 정리 누락과 더 관련이 큽니다.
  5. FIN 종료와 RST 종료는 다르며, RST는 정상적인 마무리보다 즉시 abort 에 가깝습니다.

결국 4-way handshaking은 "FIN, ACK 네 개를 외우는 절차"가 아니라, TCP가 양방향 스트림을 어떻게 안전하게 접고, 종료 후 지연 세그먼트까지 어떻게 정리하는지 를 보여 주는 상태 전이 과정에 가깝습니다.