TCP 3-way와 4-way 핸드셰이크 (TCP 3-way and 4-way Handshake)

한눈에 보기

TCP 3-way 핸드셰이크는 연결 수립, 4-way 핸드셰이크는 연결 종료 절차이다. SYN과 FIN의 교환 단계, TIME_WAIT의 의미, SYN 플러딩 공격과 방어까지 정리한다.

최종 업데이트 2026.09.17

TCP 3-way 핸드셰이크(three-way handshake)는 연결을 시작할 때 클라이언트와 서버가 세 번의 패킷 교환으로 서로의 통신 의사와 초기 순서 번호를 확인하는 절차이다. 4-way 핸드셰이크(four-way handshake)는 연결을 끝낼 때 양쪽이 각자 보낼 데이터가 없음을 알리고 확인하는 네 번의 교환 절차이다. TCP가 신뢰성 있는 양방향 통신을 보장하기 위한 시작과 끝의 약속이라고 할 수 있다.

연결 수립이 필요한 이유

TCP는 바이트 스트림을 순서대로, 빠짐없이 전달하는 것을 약속하는 프로토콜이다. 이를 위해 각 바이트에 순서 번호(sequence number)를 붙이고 수신 확인(ACK, acknowledgment)을 주고받는데, 통신을 시작하려면 양쪽이 서로의 시작 순서 번호(ISN, Initial Sequence Number)를 알고 있어야 한다. 또한 상대가 실제로 수신 준비가 되어 있는지도 확인해야 한다. 3-way 핸드셰이크는 이 두 가지, 즉 양방향 통신 가능 여부 확인과 ISN 교환을 최소 횟수로 달성하는 절차이다. ISN을 0부터 시작하지 않고 예측하기 어렵게 만드는 이유는 이전 연결의 지연 패킷과의 혼동, 그리고 순서 번호를 추측한 세션 탈취 공격을 막기 위해서이다.

3-way 핸드셰이크의 과정

  1. SYN: 클라이언트가 SYN(synchronize) 플래그를 세운 패킷에 자신의 ISN을 담아 보내고 SYN_SENT 상태가 된다.
  2. SYN+ACK: 서버는 클라이언트의 ISN에 1을 더한 확인 번호와 함께 자신의 ISN을 담은 SYN+ACK 패킷을 보내고 SYN_RECEIVED 상태가 된다.
  3. ACK: 클라이언트가 서버 ISN에 1을 더한 확인 번호로 ACK를 보내면 양쪽 모두 ESTABLISHED 상태가 되고 데이터 전송이 시작된다.

두 번의 교환으로 부족한 이유는 서버 입장에서 자신의 SYN이 클라이언트에 잘 도착했는지 알 수 없기 때문이다. 세 번째 ACK가 있어야 양쪽 모두 "내 송신과 수신이 모두 동작한다"는 확신을 갖는다. 한편 서버가 SYN을 받고 ACK를 기다리는 반쯤 열린 상태를 대량으로 만들어 자원을 고갈시키는 SYN 플러딩(SYN flooding) 공격이 있으며, 서버가 상태를 저장하지 않고 검증 정보를 순서 번호에 인코딩해 돌려보내는 SYN 쿠키(SYN cookie)로 방어한다. 최신 환경에서는 이전 연결에서 받은 쿠키로 첫 SYN에 데이터를 실어 왕복을 아끼는 TCP Fast Open도 있고, QUIC은 아예 전송 연결과 TLS 협상을 한 번의 핸드셰이크로 합쳤다.

TCP 3-way 핸드셰이크: 클라이언트가 SYN을 보내고 서버가 SYN+ACK로 답한 뒤 클라이언트가 ACK를 보내 양쪽이 ESTABLISHED가 되는 세 단계
세 번이 필요한 이유는 «양쪽 모두» 자기 초기 순서 번호를 보내고 상대가 그것을 받았다는 확인을 얻어야 하기 때문이다. 두 번이면 서버는 자기 SYN이 도착했는지 알 수 없고, 네 번째는 이미 ③이 대신하므로 낭비다.

4-way 핸드셰이크의 과정

TCP는 양방향 채널이 독립적이므로 종료도 방향별로 따로 닫는다. 클라이언트가 먼저 닫는 경우의 흐름은 다음과 같다.

TCP 4-way 핸드셰이크: 능동 종료 측이 FIN을 보내고 상대가 ACK, 남은 데이터 뒤 FIN을 보내면 마지막 ACK 후 TIME_WAIT를 거쳐 닫히는 네 단계
②와 ③이 갈라져 있는 이유가 4단계의 핵심이다. 서버는 «네 FIN을 받았다»는 ACK는 바로 보내지만, 아직 보낼 데이터가 남아 있을 수 있어 자기 FIN은 그것을 다 보낸 뒤에 보낸다. 마지막 ACK를 보낸 쪽은 그 ACK가 유실됐을 때 재전송된 FIN에 답하기 위해 TIME_WAIT에 머문다.
  1. FIN: 클라이언트가 보낼 데이터가 끝났음을 알리는 FIN(finish) 패킷을 보내고 FIN_WAIT_1 상태가 된다.
  2. ACK: 서버가 FIN을 확인하는 ACK를 보내고 CLOSE_WAIT 상태가 된다. 클라이언트는 FIN_WAIT_2로 넘어가며, 서버는 아직 남은 데이터를 마저 보낼 수 있다.
  3. FIN: 서버도 전송을 마치면 자신의 FIN을 보내고 LAST_ACK 상태가 된다.
  4. ACK: 클라이언트가 마지막 ACK를 보내고 TIME_WAIT 상태로 들어간다. 서버는 이 ACK를 받으면 즉시 연결을 닫는다.

이렇게 한쪽만 닫힌 반이중 상태(half-close)를 허용하는 덕분에, 클라이언트가 요청 전송을 끝냈다고 알린 뒤에도 서버의 긴 응답을 끝까지 받을 수 있다. 수립은 3번, 종료는 4번인 이유가 여기서 드러난다. 수립 때는 서버가 SYN과 ACK를 한 패킷에 합쳐 보낼 수 있지만, 종료 때는 상대의 FIN을 확인하는 시점과 자신이 보낼 데이터를 다 보내는 시점이 다를 수 있어 ACK와 FIN이 분리되는 것이 일반적이기 때문이다.

TIME_WAIT의 의미

마지막 ACK를 보낸 쪽은 곧바로 닫지 않고 최대 세그먼트 생존 시간의 두 배(2MSL) 동안 TIME_WAIT 상태로 대기한다. 이유는 두 가지다. 첫째, 마지막 ACK가 유실되면 상대가 FIN을 재전송하는데 이에 다시 응답해 주어야 상대가 정상 종료할 수 있다. 둘째, 네트워크에 남아 떠돌던 지연 패킷이 같은 주소와 포트 조합으로 새로 열린 연결에 끼어드는 것을 막는다. 짧은 연결을 대량으로 맺는 클라이언트나 프록시에서는 TIME_WAIT 소켓이 쌓여 포트가 고갈될 수 있어, 연결 풀링과 킵얼라이브(keep-alive)로 연결 자체를 재사용하는 것이 근본 대책이다.

수립과 종료 절차 비교

구분3-way 핸드셰이크4-way 핸드셰이크
목적연결 수립, ISN 교환연결 종료, 잔여 데이터 보장
사용 플래그SYN, SYN+ACK, ACKFIN, ACK, FIN, ACK
교환 횟수3회(SYN과 ACK 결합)4회(ACK와 FIN 분리 가능)
특수 상태SYN_RECEIVED(반개방)TIME_WAIT, CLOSE_WAIT
대표 이슈SYN 플러딩 공격TIME_WAIT 누적, CLOSE_WAIT 누수

참고로 CLOSE_WAIT가 서버에 오래 쌓인다면 상대의 FIN을 받고도 애플리케이션이 소켓을 닫지 않고 있다는 뜻이므로, 네트워크가 아니라 코드의 자원 정리 버그를 의심해야 한다. 반대로 TIME_WAIT는 정상 종료의 흔적이므로 존재 자체는 문제가 아니며, 개수가 병목이 될 때만 대응하면 된다. 또한 정상 종료 절차 외에 RST(reset) 플래그로 연결을 즉시 끊어 버리는 비정상 종료 경로도 있는데, 닫힌 포트로 패킷이 오거나 프로세스가 강제 종료될 때 관찰된다.

면접에서 이렇게 물어본다

  • 3-way 핸드셰이크의 각 단계와 상태 변화를 순서 번호와 함께 설명해 보라.
  • 연결 수립은 왜 2번의 교환으로는 부족한가.
  • 연결 종료가 4번의 교환을 거치는 이유는 무엇인가.
  • TIME_WAIT 상태는 왜 필요하고, 대량으로 쌓이면 어떤 문제가 생기는가.
  • SYN 플러딩 공격의 원리와 SYN 쿠키의 방어 방식을 설명해 보라.
네트워크 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기