본문 바로가기
카테고리 없음

TCP 3-Way Handshake — 네트워크 기초

by SuldenLion 2026. 7. 26.
반응형
TCP 3-Way Handshake — 네트워크 기초

왜 Handshake가 필요한가?

TCP는 신뢰성 있는 전송을 보장해야 한다. 이를 위해서는 데이터를 보내기 전에 먼저 상대방과 연결을 수립(Establish)하고 양측이 통신 준비가 됐음을 확인해야 한다.

3-Way Handshake는 이 과정에서 세 가지를 달성한다: 상대방이 존재하고 응답 가능함을 확인, 시퀀스 번호(Sequence Number) 동기화, 연결 파라미터(MSS, Window Size, SACK 지원 여부 등) 협상.

💡 왜 2-Way가 아닌 3-Way인가?
2-Way로는 충분하지 않다. 클라이언트가 SYN을 보내고 서버가 SYN-ACK를 보내도, 서버는 클라이언트가 자신의 SYN-ACK를 받았는지 알 수 없다. 세 번째 ACK를 통해 양측 모두 상대의 송수신 능력을 확인할 수 있다.

연결 수립: 3-Way Handshake

🖥 Client
🖥 Server
SYN_SENT

seq=x 생성
SYN
────────────▶
seq=x
SYN_RECEIVED

seq=y 생성, x 기억
ack=y+1 확인
seq=x+1 기억
SYN-ACK
◀────────────
seq=y, ack=x+1
SYN_RECEIVED
ESTABLISHED
ACK
────────────▶
seq=x+1, ack=y+1
ESTABLISHED
데이터 전송 시작
DATA
────────────▶

각 단계 상세 설명

1

SYN — 클라이언트 → 서버

클라이언트가 서버에 연결을 요청한다. SYN 플래그를 세우고, 임의로 생성한 초기 시퀀스 번호(ISN, Initial Sequence Number) seq=x를 함께 보낸다. ISN을 임의로 생성하는 이유는 이전 연결의 패킷과 혼동되지 않도록 하기 위해서다. 클라이언트 상태: SYN_SENT

2

SYN-ACK — 서버 → 클라이언트

서버가 클라이언트의 SYN을 받고 연결을 수락한다. SYN 플래그ACK 플래그를 동시에 세운다. 서버의 ISN seq=y를 보내고, 클라이언트의 ISN에 1을 더한 ack=x+1을 보내 "x까지 받았으니 x+1번째 바이트부터 보내라"고 알린다. 서버 상태: SYN_RECEIVED

3

ACK — 클라이언트 → 서버

클라이언트가 서버의 SYN-ACK를 받고 ACK를 보낸다. ack=y+1로 서버의 SYN을 확인했음을 알린다. 이 패킷이 도착하면 양측 모두 ESTABLISHED 상태가 되고 데이터 전송을 시작할 수 있다. 이 세 번째 ACK에는 이미 HTTP 요청 데이터가 포함될 수 있다(TCP Fast Open).

연결 종료: 4-Way Handshake

TCP 연결 종료는 수립보다 복잡하다. 양방향 연결을 각각 독립적으로 종료해야 하기 때문이다. 한쪽이 전송을 끝내도 반대쪽은 여전히 데이터를 보낼 수 있는 반이중(Half-Close) 상태를 허용한다.

🖥 Client (Active Close)
🖥 Server (Passive Close)
FIN_WAIT_1
FIN
────────────▶
seq=u
CLOSE_WAIT
FIN_WAIT_2
ACK
◀────────────
ack=u+1
남은 데이터 전송 가능
FIN
◀────────────
seq=v
LAST_ACK
TIME_WAIT

2MSL 대기 후 CLOSED
ACK
────────────▶
ack=v+1
CLOSED

TIME_WAIT 상태

연결을 먼저 닫는 쪽(Active Close)은 마지막 ACK를 보낸 후 바로 CLOSED가 되지 않고 TIME_WAIT 상태로 2MSL(Maximum Segment Lifetime, 보통 60~120초)을 기다린다.

이유는 두 가지다: 마지막 ACK가 유실됐을 때 서버가 FIN을 재전송하면 이를 받아 다시 ACK를 보낼 수 있어야 한다. 네트워크에 떠돌아다니는 이전 연결의 지연 패킷이 새 연결에 영향을 주지 않도록 한다.

⚠️ TIME_WAIT 고갈 문제
서버가 짧은 연결을 매우 많이 맺는 환경(API 게이트웨이, 마이크로서비스)에서 TIME_WAIT 소켓이 쌓여 포트 번호가 부족해지는 문제가 발생할 수 있다. SO_REUSEADDR 옵션이나 tcp_tw_reuse 커널 파라미터로 완화할 수 있다.

TCP 상태 전이표

상태설명발생 시점
CLOSED연결 없음초기 상태 또는 연결 종료 후
SYN_SENTSYN 전송, 응답 대기클라이언트: connect() 호출 후
SYN_RECEIVEDSYN 수신, SYN-ACK 전송서버: SYN 수신 후
ESTABLISHED연결 수립, 데이터 전송 가능3-Way Handshake 완료 후
FIN_WAIT_1FIN 전송, ACK 대기Active Close 측: close() 호출 후
FIN_WAIT_2ACK 수신, 상대 FIN 대기Active Close 측
CLOSE_WAITFIN 수신, 앱이 close() 호출 전Passive Close 측
TIME_WAIT2MSL 대기 중Active Close 측: 마지막 ACK 전송 후
CLOSED완전 종료TIME_WAIT 만료 후

시퀀스 번호와 ACK 메커니즘

TCP는 신뢰성을 위해 모든 바이트에 시퀀스 번호(Sequence Number)를 부여한다. 수신 측은 올바르게 받은 마지막 바이트 번호+1을 ACK 번호로 돌려보낸다. 이를 통해 송신 측은 어디까지 전달됐는지 알 수 있다.

# Wireshark로 3-Way Handshake 캡처 예시
# (실제 캡처 시 wireshark 또는 tcpdump 사용)

$ tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack|tcp-fin) != 0' -n

# 출력 예시:
# 14:23:01 192.168.1.10.52341 > 93.184.216.34.443: Flags [S],  seq 123456789
# 14:23:01 93.184.216.34.443 > 192.168.1.10.52341: Flags [S.], seq 987654321, ack 123456790
# 14:23:01 192.168.1.10.52341 > 93.184.216.34.443: Flags [.],  ack 987654322
# Flags: S=SYN, .=ACK, F=FIN, R=RST, P=PSH

SYN Flooding 공격

3-Way Handshake의 설계를 악용한 DoS 공격이다. 공격자가 위조된 출발지 IP로 대량의 SYN 패킷을 보내면, 서버는 SYN-ACK를 보내고 SYN_RECEIVED 상태의 연결을 Backlog 큐에 쌓는다. ACK는 영영 오지 않으므로 큐가 가득 차 정상 연결을 받지 못하게 된다.

🛡 SYN Cookie로 방어
SYN Cookie는 서버가 SYN_RECEIVED 상태를 Backlog에 저장하지 않고, 시퀀스 번호에 암호화된 정보를 인코딩해 보내는 기법이다. 실제 ACK가 오면 Cookie를 검증해 연결을 수립한다. Linux에서 net.ipv4.tcp_syncookies=1로 활성화할 수 있다.

TCP Fast Open (TFO)

일반 3-Way Handshake는 데이터를 보내기 전에 1 RTT(Round Trip Time)를 소비한다. TCP Fast Open은 최초 연결 시 쿠키를 발급받고, 이후 연결에서는 SYN 패킷에 데이터를 실어 보내 핸드셰이크 지연을 줄이는 기법이다. HTTP/1.1에서 HTTPS 연결 시 체감되는 지연 개선에 활용된다.

# Linux에서 TCP Fast Open 활성화
$ sysctl net.ipv4.tcp_fastopen=3
# 1: 클라이언트만, 2: 서버만, 3: 양쪽 모두

# Python 소켓으로 TFO 사용
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_FASTOPEN, 5)
s.sendto(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n",
         socket.MSG_FASTOPEN, ("example.com", 80))
✅ 핵심 요약
TCP 3-Way Handshake는 SYN → SYN-ACK → ACK 세 단계로 연결을 수립하며, 시퀀스 번호 동기화와 연결 파라미터 협상을 수행한다. 종료는 FIN → ACK → FIN → ACK 4단계로 이루어지며, Active Close 측은 TIME_WAIT 상태로 2MSL을 대기한다. SYN Flooding 방어를 위한 SYN Cookie, 지연 최소화를 위한 TCP Fast Open도 중요한 개념이다.
반응형

댓글