base

TCP 4-Way Handshake

TCP(Transmission Control Protocol)는 신뢰성 있는 연결 지향형 통신을 제공하기 위해 설계된 프로토콜로, 연결을 설정하고 데이터를 주고받은 뒤에는 반드시 정상적인 연결 종료 과정을 거칩니다. 많은 개발자들이 연결을 여는 과정인 3-Way Handshake는 잘 알고 있지만, 연결을 닫는 과정인 4-Way Handshake는 다소 생소할 수 있습니다. 그러나 연결 종료 역시 설정만큼이나 중요하며, 잘못된 종료는 데이터 유실이나 시스템 리소스 낭비로 이어질 수 있습니다.


이 글에서는 TCP 연결 종료 시 수행되는 4단계의 과정을 자세히 살펴보고, 왜 TCP가 연결 해제를 이처럼 신중하게 처리하는지를 기술적으로 정리해 봅니다.


왜 4단계가 필요한가?

TCP는 양방향 스트림 기반 프로토콜입니다. 즉, 클라이언트와 서버는 서로 독립적으로 데이터를 송수신할 수 있는 상태를 유지합니다. 이 때문에 어느 한쪽이 데이터를 더 이상 보내지 않더라도, 다른 한쪽은 계속 데이터를 보낼 수 있습니다. 이러한 이유로, 연결을 완전히 종료하려면 양방향 모두에 대해 독립적인 종료 절차를 수행해야 합니다. 이로 인해 TCP는 4-Way Handshake라는 4단계 종료 과정을 채택합니다.


TCP 연결 해제 흐름: 단계별 설명

다음은 TCP에서 연결을 해제하는 4단계의 흐름입니다:

1. FIN (A → B)

연결을 종료하려는 측(A)은 FIN 플래그가 설정된 TCP 세그먼트를 B에게 전송합니다. 이는 “더 이상 전송할 데이터가 없습니다”라는 의사 표현입니다. 이때 A는 반쪽 닫기(Half-Close) 상태로 전환되며, 수신은 계속 가능하지만 송신은 불가능합니다.

2. ACK (B → A)

B는 A로부터 받은 FIN 요청에 대해 ACK 응답을 보냅니다. 이는 “당신이 더 이상 데이터를 보내지 않겠다는 의사를 확인했다”는 의미입니다. 하지만 B는 여전히 데이터를 전송할 수 있으며, A는 그 데이터를 수신할 수 있습니다.

3. FIN (B → A)

B도 자신의 데이터 전송이 완료되었다고 판단되면, 자신의 FIN 플래그가 설정된 세그먼트를 A에게 전송합니다. 이는 “나도 데이터를 더 이상 보내지 않겠다”는 의사 표현입니다.

4. ACK (A → B)

A는 B의 FIN에 대해 확인 응답 ACK를 전송하고, 이로써 양방향 데이터 흐름이 모두 종료되며 TCP 연결은 완전히 닫힙니다.


반쪽 닫기(Half-Close)의 개념

FIN과 ACK가 각각 두 번씩 오가는 이유는 TCP가 양방향 통신 채널을 유지하기 때문입니다. 한 방향에서 송신이 끝났다고 해서 다른 방향의 데이터 흐름도 자동으로 종료되는 것이 아닙니다. 따라서 A → B 방향의 종료와 B → A 방향의 종료는 서로 독립적인 절차로 처리됩니다.

이러한 구조는 잔여 데이터가 완전히 전송되고 수신될 수 있는 시간을 확보해주며, 특히 지연된 패킷 처리나 버퍼에 남은 데이터 전송을 보장할 수 있습니다.


특징

TCP는 단순한 연결을 넘어, 신뢰성과 데이터 정합성을 보장하기 위해 설계된 프로토콜입니다. 종료 단계에서도 이러한 특성을 유지하기 위해 다음과 같은 설계 원칙이 적용됩니다.

  • 데이터 유실 방지: 송신자가 보낸 마지막 바이트까지 수신자가 받을 수 있도록 시간과 절차를 확보합니다.
  • 정확한 상태 전이: 각 상태(TIME_WAIT, CLOSE_WAIT 등)를 통해 시스템은 연결 상태를 명확하게 추적합니다.
  • 리소스 정리 보장: 종료가 명확히 이루어져야 포트나 메모리 버퍼와 같은 커널 자원을 회수할 수 있습니다.

TCP 종료 절차가 없거나 부정확하게 처리되면, 시스템은 '좀비 연결(zombie connection)'을 유지하거나 중간에 유실된 데이터로 인해 애플리케이션 레벨에서 문제가 발생할 수 있습니다.


확인방법

네트워크 개발자나 인프라 엔지니어가 실제 환경에서 TCP 종료 절차를 분석할 때는 Wireshark 같은 툴을 활용합니다. 이를 통해 다음과 같은 항목을 확인할 수 있습니다.

  • FIN/ACK 교환 순서와 시점
  • TIME_WAIT: 마지막 ACK 이후 일정 시간 동안 포트를 유지해 재전송 대응
  • CLOSE_WAIT → LAST_ACK: 수신 측에서 FIN 수신 후 애플리케이션이 close()를 호출하지 않았을 때의 문제

이러한 분석은 장애 원인 파악이나 성능 튜닝, 또는 애플리케이션의 정상 종료 여부를 판단하는 데 매우 중요합니다.


정리

TCP 연결을 종료할 때는 송신과 수신 양쪽 스트림을 차례로 닫으며, 데이터 손실 없이 통신을 마무리하기 위해 네 번의 제어 메시지를 주고받습니다. 먼저 연결을 닫고자 하는 쪽이 FIN 플래그를 설정한 패킷을 보내 “이제 이 방향으로는 보낼 데이터가 더 없다”고 알리고, 상대방은 이를 받으면 ACK로 “FIN을 잘 받았음”을 응답합니다. 그다음 상대방도 자신의 전송 스트림을 종료하기 위해 또 한 번 FIN을 보내고, 처음 FIN을 보낸 쪽은 마지막으로 ACK를 전송해 “내 FIN도 잘 받았음”을 확인합니다. 이후 ACK를 보낸 쪽은 일정 시간 동안 TIME_WAIT 상태로 머물러 재전송된 패킷이 남아 있더라도 처리할 수 있도록 함으로써, 양방향으로 남은 모든 데이터가 확실히 전송된 후에야 연결을 완전히 해제합니다.


참고 자료