"TCP는 연결을 맺고 통신한다"는 말은 들었지만, 그 '연결'이 정확히 무엇인지는 흐릿합니다. TCP 연결은 물리적인 선이 아니라 양쪽이 서로 준비됐음을 확인한 합의 상태이고, 그 합의를 만드는 절차가 3-way handshake입니다.
핵심: 세 번 주고받는 이유
연결을 맺으려면 양쪽이 "보낼 준비 됐다"와 "받을 준비 됐다"를 서로 확인해야 합니다. 한 번이나 두 번으로는 한쪽의 수신 능력을 보장할 수 없어, 최소 세 번의 교환이 필요합니다.
| 단계 | 방향 | 플래그 | 의미 |
|---|---|---|---|
| 1 | 클라이언트 → 서버 | SYN | "연결하고 싶다. 내 순서번호는 x" |
| 2 | 서버 → 클라이언트 | SYN-ACK | "좋다(ACK x+1). 내 순서번호는 y" |
| 3 | 클라이언트 → 서버 | ACK | "확인했다(ACK y+1). 시작하자" |
1단계에서 클라이언트가 SYN을 보내 연결 의사와 순서번호를 알립니다. 2단계에서 서버가 그걸 받았다는 ACK와 자기 SYN을 함께 보냅니다(그래서 SYN-ACK). 3단계에서 클라이언트가 서버의 SYN까지 확인하는 ACK를 보내면, 양쪽 모두 상대의 송수신 능력을 확인한 셈이라 연결이 성립합니다.
왜 두 번으로는 안 되나
두 번만 주고받으면 서버는 클라이언트가 자기 응답(SYN-ACK)을 받았는지 알 수 없습니다. 세 번째 ACK가 있어야 비로소 "내 패킷도 상대에게 도달했다"가 양방향으로 확인됩니다. 이 합의된 순서번호(x, y)는 이후 데이터의 순서를 맞추고 빠진 패킷을 재전송하는 기준이 됩니다. 연결을 끊을 때는 비슷하게 FIN을 주고받는 4-way 과정을 거칩니다.
직접 확인하기
실제 SYN 패킷이 오가는 모습은 tcpdump로 잡아 볼 수 있습니다.
sudo tcpdump -i any 'tcp port 443' -n
IP 10.0.0.5.51234 > 93.184.216.34.443: Flags [S], seq 1000
IP 93.184.216.34.443 > 10.0.0.5.51234: Flags [S.], seq 8000, ack 1001
IP 10.0.0.5.51234 > 93.184.216.34.443: Flags [.], ack 8001
Flags [S]가 SYN, [S.]가 SYN-ACK, [.]가 ACK입니다. ack 1001이 클라이언트 seq 1000에 1을 더한 값인 점, ack 8001이 서버 seq 8000에 1을 더한 값인 점이 표의 x+1·y+1과 정확히 맞아떨어집니다. 연결이 맺어진 뒤 상태는 ss -t의 ESTAB으로도 볼 수 있습니다.
요점 정리
- TCP 연결은 선이 아니라 양방향 준비 확인이라는 합의 상태.
- SYN → SYN-ACK → ACK 세 단계로 양쪽의 송수신 능력을 서로 확인.
- 두 번으로는 한쪽의 수신 확인이 빠져 세 번이 필요.
tcpdump의[S]·[S.]·[.]플래그로 핸드셰이크를 직접 관찰하세요.
TCP 핸드셰이크와 패킷을 직접 캡처해 분석하는 실습은 네트워크 트랙에서 회원가입 없이 무료로 익힐 수 있습니다.