3-Way Handshake

TCP 연결을 수립할 때 클라이언트와 서버가 SYN → SYN-ACK → ACK 세 단계의 패킷을 교환하여 신뢰성 있는 양방향 통신을 준비하는 과정입니다.

왜 필요한가

TCP는 신뢰성 있는 통신을 보장해야 함
→ 데이터를 보내기 전에 먼저 확인해야 할 것들:

1. 상대방이 살아있는가?
2. 상대방이 데이터를 받을 준비가 되었는가?
3. 양쪽 모두 보내기·받기가 가능한가?
4. 서로의 초기 시퀀스 번호를 알고 있는가?

→ 이 모든 것을 확인하는 과정이 3-way handshake

전체 흐름

클라이언트                                    서버
(CLOSED)                                    (LISTEN)
    │                                          │
    │  ① SYN (seq=100)                         │
    │ ──────────────────────────────────→       │
    │  "연결하고 싶어, 내 시퀀스 번호는 100이야"   │
    │                                          │
(SYN_SENT)                                     │
    │                                          │
    │  ② SYN + ACK (seq=300, ack=101)          │
    │ ←──────────────────────────────────      │
    │  "좋아, 내 시퀀스 번호는 300이야,           │
    │   너의 100 잘 받았어, 다음엔 101 보내줘"    │
    │                                          │
    │                                    (SYN_RCVD)
    │                                          │
    │  ③ ACK (seq=101, ack=301)                │
    │ ──────────────────────────────────→       │
    │  "너의 300 잘 받았어, 다음엔 301 보내줘"    │
    │                                          │
(ESTABLISHED)                           (ESTABLISHED)
    │                                          │
    │  ◄────── 데이터 전송 시작 ──────►         │

각 단계 상세

Step 1: SYN (클라이언트 → 서버)

┌─── TCP 세그먼트 ────────────────────┐
│ SYN 플래그:  1 (ON)                  │
│ ACK 플래그:  0 (OFF)                 │
│ seq:        100 (ISN, 초기 시퀀스)    │
│ ack:        없음                     │
└──────────────────────────────────────┘

클라이언트: CLOSED → SYN_SENT
서버: LISTEN (변화 없음)
항목설명
목적연결 요청 + 클라이언트 ISN 전달
SYN 플래그"동기화하자"는 의미 (Synchronize)
seq=100클라이언트의 초기 시퀀스 번호 (ISN)

Step 2: SYN-ACK (서버 → 클라이언트)

┌─── TCP 세그먼트 ────────────────────┐
│ SYN 플래그:  1 (ON)                  │
│ ACK 플래그:  1 (ON)                  │
│ seq:        300 (서버의 ISN)         │
│ ack:        101 (클라이언트 seq+1)    │
└──────────────────────────────────────┘

클라이언트: SYN_SENT (변화 없음)
서버: LISTEN → SYN_RCVD
항목설명
목적연결 수락 + 서버 ISN 전달 + 클라이언트 SYN 확인
SYN서버도 동기화 요청
ACK클라이언트의 SYN을 잘 받았다는 응답
seq=300서버의 초기 시퀀스 번호
ack=101"너의 100번까지 받았어, 다음엔 101번 보내줘"

Step 3: ACK (클라이언트 → 서버)

┌─── TCP 세그먼트 ────────────────────┐
│ SYN 플래그:  0 (OFF)                 │
│ ACK 플래그:  1 (ON)                  │
│ seq:        101                      │
│ ack:        301 (서버 seq+1)         │
└──────────────────────────────────────┘

클라이언트: SYN_SENT → ESTABLISHED
서버: SYN_RCVD → ESTABLISHED
항목설명
목적서버의 SYN-ACK에 대한 확인 응답
seq=101서버가 ack=101로 요청했으므로 101 전송
ack=301"너의 300번까지 받았어, 다음엔 301번 보내줘"

상태 변화 전체 정리

클라이언트          패킷           서버
─────────       ─────────      ─────────
CLOSED                          LISTEN
    │                              │
    │── SYN ──────────────→        │
SYN_SENT                          │
    │                              │
    │        ←────────── SYN+ACK ──│
    │                          SYN_RCVD
    │                              │
    │── ACK ──────────────→        │
ESTABLISHED                   ESTABLISHED
상태의미
CLOSED연결 없음
LISTEN서버가 연결 대기 중
SYN_SENT클라이언트가 SYN 보내고 응답 대기
SYN_RCVD서버가 SYN 받고 SYN-ACK 보낸 후 대기
ESTABLISHED연결 수립 완료, 데이터 전송 가능

ISN (Initial Sequence Number)이 랜덤인 이유

[ 만약 ISN이 항상 0이면 ]

이전 연결:  클라이언트(seq=50) ──→ 서버
연결 종료 후 ...
네트워크에 남아있던 지연 패킷:  seq=50이 뒤늦게 도착

새 연결:    클라이언트(seq=0부터 시작)
서버:       "seq=50? 이건 새 연결의 패킷이야" → 잘못된 데이터 수신

[ ISN이 랜덤이면 ]

이전 연결:  ISN=38291 → seq=38341 패킷 지연
새 연결:    ISN=72058 → seq 범위가 완전히 다름
서버:       "seq=38341? 이건 현재 연결과 무관" → 무시 O
이유설명
이전 연결 패킷 구분지연된 이전 패킷이 새 연결에 혼입되는 것 방지
보안예측 가능한 ISN은 TCP 세션 하이재킹 공격에 취약
고유성각 연결이 고유한 시퀀스 공간을 가짐

Sequence Number와 Acknowledgment Number

seq와 ack의 관계:

ack = 상대방이 보낸 seq + 받은 데이터 바이트 수

3-way handshake에서는 SYN 자체가 1바이트를 소비한다고 간주
→ ack = 상대방 seq + 1
클라이언트                              서버

seq=100 (SYN) ──────────────→
                              ack = 100 + 1 = 101
              ←────────────── seq=300, ack=101 (SYN+ACK)

ack = 300 + 1 = 301
seq=101, ack=301 (ACK) ────→

연결 후 데이터 전송:
seq=101, 50바이트 전송 ─────→
                              ack = 101 + 50 = 151
              ←────────────── ack=151 ("151번부터 보내줘")

3-way인 이유 — 왜 2-way가 아닌가

2-way의 문제

[ 2-way handshake ]

클라이언트               서버
    │── SYN ───→          │  ① 연결 요청
    │          ←── ACK ───│  ② 서버 수락
(ESTABLISHED)       (ESTABLISHED)

문제 시나리오:
클라이언트가 SYN을 보냈지만 네트워크 지연
→ 타임아웃 → 재전송 SYN
→ 원래 SYN이 뒤늦게 도착

    │── SYN (원본, 지연) ────→  │
    │── SYN (재전송) ──────→    │
    │          ←── ACK (재전송) ─│  정상 연결 O
    │                            │
    │   ... 시간 경과 ...          │
    │                            │
    │── SYN (원본, 뒤늦게 도착) →  │
    │          ←── ACK ──────────│  서버: "새 연결이네!" → 불필요한 연결 생성 X
                                     ↑ 클라이언트는 이 연결을 모름
                                       서버 자원 낭비 (Half-Open)

3-way로 해결

[ 3-way handshake ]

지연된 SYN이 도착해도:

    │── SYN (원본, 뒤늦게 도착) ─→  │
    │          ←── SYN+ACK ─────── │  서버: SYN_RCVD
    │                               │
    │  (클라이언트는 이 연결을 요청한 적 없음)
    │  → ACK를 보내지 않음 (또는 RST 전송)
    │                               │
    │                               │  서버: ACK 안 옴 → 타임아웃 → 연결 폐기
                                        ↑ 불필요한 연결 생성 방지 O
정리:
2-way: 서버가 일방적으로 연결 수립 → 유령 연결 발생 가능
3-way: 클라이언트의 최종 확인(ACK)이 있어야 연결 확정 → 안전

3-way가 확인하는 것

Step 1 (SYN):
클라이언트 → 서버 전송 가능 확인     O

Step 2 (SYN-ACK):
서버 → 클라이언트 전송 가능 확인     O
서버가 클라이언트의 데이터를 수신 가능  O

Step 3 (ACK):
클라이언트가 서버의 데이터를 수신 가능  O

→ 양방향 통신 가능 확인 완료
              클라이언트 송신  서버 송신  클라이언트 수신  서버 수신
Step 1 (SYN)      O
Step 2 (SYN-ACK)              O                       O
Step 3 (ACK)                             O

→ 3단계면 4가지를 모두 확인 가능
→ 이론적 최소 횟수가 3회

TCP 헤더 플래그 상세

TCP 헤더 (20바이트)
┌────────────────────────────────────────┐
│ 출발지 포트 (16비트) │ 목적지 포트 (16비트) │
├────────────────────────────────────────┤
│ Sequence Number (32비트)                │
├────────────────────────────────────────┤
│ Acknowledgment Number (32비트)          │
├────────────────────────────────────────┤
│ 헤더길이│예약│  제어 플래그  │ 윈도우 크기  │
│        │    │URG ACK PSH RST SYN FIN│   │
├────────────────────────────────────────┤
│ 체크섬           │ 긴급 포인터           │
└────────────────────────────────────────┘
플래그의미3-way에서 사용
SYN동기화, 연결 시작Step 1, 2
ACK확인 응답Step 2, 3
FIN연결 종료4-way handshake에서 사용
RST연결 강제 리셋비정상 종료 시
PSH즉시 전달데이터 전송 시
URG긴급 데이터긴급 데이터 전송 시

3-way handshake에서 발생할 수 있는 문제

1. SYN Flood 공격

공격자가 대량의 SYN을 보내고 ACK는 보내지 않음

공격자 ── SYN ──→ 서버: SYN_RCVD (자원 할당)
공격자 ── SYN ──→ 서버: SYN_RCVD (자원 할당)
공격자 ── SYN ──→ 서버: SYN_RCVD (자원 할당)
... 수만 개 ...

서버: SYN_RCVD 상태가 가득 참 (Backlog Queue 포화)
→ 정상 사용자의 연결 요청 거부 (DoS)
대응 방법설명
SYN CookieSYN_RCVD 상태를 저장하지 않고 seq에 정보를 인코딩
Backlog Queue 증가대기열 크기 확대
SYN 타임아웃 단축SYN_RCVD 대기 시간 줄임
방화벽/IDS비정상 SYN 패턴 차단

2. ACK 유실

클라이언트                           서버
    │── SYN ──────→                   │
    │          ←── SYN+ACK ───        │
    │── ACK ──────→ (유실!)           │
    │                                  │
(ESTABLISHED)                     (SYN_RCVD)
    │                              타이머 동작 중...
    │── 데이터 전송 ──→               │
    │                              서버: "데이터에 ACK가 포함되어 있네"
    │                              → ESTABLISHED로 전환 O

Step 3의 ACK가 유실되더라도 클라이언트가 데이터를 보내면 그 패킷의 ACK 번호로 연결이 확인됩니다.

연결 수립 후 → 데이터 전송 → 연결 종료

[ 3-way handshake ] → [ 데이터 전송 ] → [ 4-way handshake ]
    연결 수립              통신              연결 종료

클라이언트        서버           클라이언트        서버
 SYN ────→                      FIN ────→
     ←──── SYN+ACK                  ←──── ACK
 ACK ────→                          ←──── FIN
                                ACK ────→
ESTABLISHED     ESTABLISHED    CLOSED       CLOSED

핵심 정리

3-way handshake는 SYN → SYN-ACK → ACK 세 단계로 TCP 연결을 수립하는 과정입니다. 양방향 통신 가능 여부를 확인하고, 서로의 ISN(초기 시퀀스 번호)을 교환하며, 2-way로는 방지할 수 없는 유령 연결 문제를 해결합니다. 이론적으로 양방향 4가지 확인(송신·수신 × 2)을 3단계에 담는 최소 횟수이며, SYN Flood 같은 공격에는 SYN Cookie 등으로 대응합니다.

CSLOG에서 보기