관리 메뉴

피터의 개발이야기

[Network] 16. 대화를 시작하기 전에 왜 세 번이나 인사할까? 본문

DevOps/Network

[Network] 16. 대화를 시작하기 전에 왜 세 번이나 인사할까?

기록하는 백앤드개발자 2026. 7. 19. 20:16
반응형

TCP/IP를 우편 시스템으로 이해하기 — Part 5. 믿을 수 없는 길 위에서 믿을 수 있게 보내기

ㅁ 들어가며

TCP는 Application에 순서 있는 신뢰 가능한 Byte Stream을 제공한다.

그러려면 양쪽이 각 Byte의 위치를 알아야 한다.

Client가 보낸 첫 Byte와 Server가 보낸 첫 Byte는 서로 다른 Sequence Space에서 시작한다.

 

데이터를 보내기 전에 다음 질문에 답해야 한다.

상대가 해당 IP와 Port에서 연결을 받을 준비가 되었는가?
Client가 사용할 초기 Sequence Number는 무엇인가?
Server가 사용할 초기 Sequence Number는 무엇인가?
양쪽 방향의 패킷과 응답이 도달할 수 있는가?
어떤 TCP Option을 사용할 수 있는가?

 

TCP는 3-Way Handshake로 이 정보를 교환한다.

Client → Server : SYN
Server → Client : SYN-ACK
Client → Server : ACK

 

세 줄만 외우면 동작의 모양은 기억할 수 있다.

하지만 왜 세 번째 ACK가 필요한지,

SYN이 Sequence Number 하나를 소비한다는 것이 무엇인지 설명하기는 어렵다.

 

이번 글에서는

Initial Sequence Number(ISN), SYN, SYN-ACK, ACK, TCP State, MSS, Window Scale, SACK Permitted

실제 숫자로 추적하고 Handshake 실패가 Timeout과 Refused로 다르게 보이는 이유를 살펴본다.


ㅁ 전화 연결보다 두 개의 등기 장부에 가깝다

TCP 연결을 전화 통화에 비유하면 서로 연결한 뒤 대화한다는 점은 이해하기 쉽다.

하지만 TCP는 양쪽 방향의 Byte를 각각 번호로 관리한다.

 

Client가 보내는 Byte의 번호와 Server가 보내는 Byte의 번호는 별도다.

Client → Server Sequence Space: Client ISN에서 시작
Server → Client Sequence Space: Server ISN에서 시작

 

두 사람이 각자 발송 장부를 하나씩 갖고 있다고 생각해 보자.

Client는 “나는 번호 1000부터 보내겠다”고 알리고,

Server는 “나는 번호 5000부터 보내겠다”고 알린다.

 

상대는 다음에 받을 번호를 ACK로 확인한다.

Client의 장부 시작 번호를 Server가 확인
Server의 장부 시작 번호를 Client가 확인

 

한 방향의 번호만 맞춰서는 양방향 Byte Stream을 만들 수 없다.

3-Way Handshake는 연결 가능성 확인과 함께 서로 독립적인 두 Sequence Space의 시작점을 동기화하는 과정이다.


ㅁ Handshake 전 양쪽은 어떤 상태일까?

Server Application은 Local Address와 Port에 Bind하고 Listen한다.

Server: 203.0.113.20:443
TCP State: LISTEN

 

Client는 연결 전 Socket에서 Server Endpoint로 Active Open을 시도한다.

Client: 192.0.2.10:52000
Server: 203.0.113.20:443

 

TCP 연결을 구분하는 4-Tuple은 다음과 같다.

Source IP       192.0.2.10
Source Port     52000
Destination IP  203.0.113.20
Destination Port 443

Client가 connect()를 호출하면 운영체제의 TCP Stack이 Handshake를 시작한다.

Application이 SYN Packet을 직접 만드는 것이 아니다. 일반적인 Socket API에서는 Kernel TCP Stack이 상태와 재전송을 관리한다.


ㅁ 첫 번째 인사 — SYN

Client는 자신의 Initial Sequence Number를 선택한다.

설명을 위해 Client ISN을 1000이라고 가정하자.

Client → Server
Flags: SYN
Sequence Number: 1000

 

Client의 TCP State는 일반적으로 다음처럼 바뀐다.

CLOSED → SYN-SENT

 

SYN은 다음 의미를 전달한다.

이 Endpoint와 TCP 연결을 시작하고 싶다.
내 Sequence Space의 시작점은 1000이다.
내가 지원하거나 제안하는 TCP Option은 이것이다.

SYN Segment에 Application Data가 없어도 SYN Flag 자체는 Sequence Number 하나를 소비한다.

따라서 Server가 정상적으로 SYN을 받았다면 다음에 기대하는 번호는 1001이다.

Client SYN seq 1000
→ Server가 기대하는 다음 번호 1001

SYN과 FIN이 Sequence Space에서 하나를 소비하기 때문에 연결 제어도 ACK로 명확하게 확인할 수 있다.


ㅁ 두 번째 인사 — SYN-ACK

Server의 Port 443에 Listening Socket이 있고 SYN을 받아들일 수 있다고 하자.

Server도 자신의 ISN을 선택한다. 설명을 위해 5000이라고 가정한다.

 

Server가 보내는 Segment는 두 역할을 함께 한다.

Server → Client
Flags: SYN, ACK
Sequence Number: 5000
Acknowledgment Number: 1001

 

ㅇ ACK 1001의 의미

Client의 SYN seq 1000을 받았다.
다음에는 seq 1001부터 보내 달라.

 

ㅇ SYN seq 5000의 의미

나도 연결할 준비가 되었다.
내 Sequence Space의 시작점은 5000이다.

 

Server는 연결 정보를 만들고 일반적으로 다음 상태로 이동한다.

LISTEN → SYN-RECEIVED

아직 Server 입장에서 Handshake가 완전히 끝난 것은 아니다.

Client가 Server의 SYN-ACK을 실제로 받았는지 확인하지 못했기 때문이다.


ㅁ 세 번째 인사 — ACK

Client가 SYN-ACK을 받으면 다음 사실을 알 수 있다.

  • 처음 보낸 SYN이 Server에 도착했다.
  • Server의 응답이 Client로 돌아오는 경로도 동작했다.
  • Server가 Port에서 연결을 받을 준비가 있다.
  • Server의 ISN은 5000이다.

Client는 Server의 SYN을 확인하는 ACK를 보낸다.

Client → Server
Flags: ACK
Sequence Number: 1001
Acknowledgment Number: 5001

 

ACK 5001의 의미는 다음과 같다.

Server의 SYN seq 5000을 받았다.
다음에는 seq 5001부터 보내 달라.

 

Client는 SYN-ACK을 받고 ACK를 보낸 시점에 일반적으로 ESTABLISHED 상태가 된다.

Client: SYN-SENT → ESTABLISHED

 

Server가 마지막 ACK를 받으면 자신의 SYN이 Client에 도착했음을 확인하고 ESTABLISHED 상태가 된다.

Server: SYN-RECEIVED → ESTABLISHED

 

이제 양쪽은 서로의 Sequence Space 시작점을 확인했다.

Client의 다음 Data seq: 1001
Server의 다음 Data seq: 5001

 

세 번째 ACK는 TCP Header만 있는 순수 ACK일 수 있고 Client의 첫 Application Data를 함께 실을 수도 있다.


ㅁ 왜 두 번으로 끝낼 수 없을까?

SYN과 SYN-ACK까지만 교환했다고 생각해 보자.

Client는 Server의 SYN-ACK을 받았으므로 양방향 경로가 동작하고 Server의 ISN이 5000이라는 사실을 안다.

 

하지만 Server는 자신의 SYN-ACK이 Client에게 도착했는지 알 수 없다.

Client → Server : SYN 도착
Server → Client : SYN-ACK 전송
                 실제 도착 여부를 Server는 모름

SYN-ACK이 중간에서 사라졌는데 Server가 연결을 완성했다고 판단하면

Client는 Server ISN을 모르는 상태인데 Server만 연결됐다고 생각할 수 있다.

 

세 번째 ACK가 Server에 도착해야 Server는 다음을 확인한다.

  • Server → Client 방향으로 보낸 SYN이 도착했다.
  • Client가 Server의 Sequence Number를 받았다.
  • Client → Server 방향의 ACK도 돌아왔다.

세 메시지는 각 방향의 ISN을 알리고 상대가 그 ISN을 받았다는 확인을 완성한다.

이를 단순히 “양쪽이 송신과 수신을 모두 할 수 있는지 세 번 확인한다”고 표현할 수 있지만

핵심은 두 개의 독립적인 Sequence Space를 양쪽 모두가 확인하는 데 세 번째 ACK가 필요하다는 점이다.


ㅁ Initial Sequence Number는 왜 0부터 시작하지 않을까?

실제 ISN은 예제처럼 항상 1000이나 5000으로 시작하지 않는다.

TCP Stack은 연결마다 변하는 32비트 ISN을 선택한다.

 

ISN을 변화시키는 이유는 다음과 같다.

  • 이전 연결에서 늦게 도착한 Segment와 새로운 연결의 Segment를 구분하는 데 도움을 준다.
  • Sequence Number를 쉽게 예측해 위조 Segment를 삽입하는 공격을 어렵게 한다.

Packet Capture 도구는 읽기 쉽게 실제 Sequence Number를 0부터 시작하는 Relative Sequence Number로 표시할 수 있다.

실제 Client ISN: 2873459012
Capture 표시    : Seq=0
다음 표시       : Seq=1

Wireshark에서 Seq=0, Ack=1이 보인다고 실제 TCP Header의 ISN이 0이라는 뜻은 아닐 수 있다.

Raw Sequence Number와 Relative Sequence Number를 구분해야 한다.


ㅁ Handshake에서는 어떤 TCP Option을 나눌까?

SYN과 SYN-ACK에는 연결에서 사용할 기능과 한계를 알리는 TCP Option이 들어갈 수 있다.

 

ㅇ MSS — Maximum Segment Size

수신 측이 하나의 TCP Segment에서 받을 수 있는 TCP Payload 크기를 알린다.

각 방향에서 상대가 광고한 MSS를 고려해 Segment 크기를 정한다.

MSS는 Ethernet Frame 전체 크기나 IP Packet 전체 크기가 아니라 TCP Data 크기 기준이다.

 

ㅇ Window Scale

TCP Header의 16비트 Window Size만으로 큰 대역폭·지연 경로의 수신 Window를 충분히 표현하기 어려울 때 배율을 협상한다.

Window Scale Option은 SYN 교환 시 알려야 연결에서 사용할 수 있다.

 

ㅇ SACK Permitted

수신 측이 Selective Acknowledgment(SACK)을 지원함을 알린다.

연속되지 않은 여러 수신 구간을 알려 손실 복구를 더 효율적으로 할 수 있다.

 

ㅇ Timestamps

RTT 측정과 오래된 중복 Segment 구분 등에 사용할 Timestamp 정보를 교환할 수 있다.

SYN Option 교환
├─ MSS
├─ Window Scale
├─ SACK Permitted
└─ Timestamps

Handshake는 연결을 열기만 하는 과정이 아니라 이후 Byte Stream을 운영할 조건도 준비한다.

양쪽 SYN에 어떤 Option이 있었는지 확인하면 성능 문제와 중간 장비의 Option 제거 문제를 조사하는 데 도움이 된다.


ㅁ SYN에 응답이 없으면 무엇을 알 수 있을까?

Client가 SYN을 보냈지만 아무 응답도 받지 못했다고 하자.

Client는 SYN이 손실되었는지, Server가 응답했지만 Return Path에서 손실되었는지 알 수 없다.

 

일정 Timer 뒤 SYN을 재전송한다.

Client → Server : SYN
                  응답 없음
Client → Server : SYN Retransmission
                  응답 없음
...
Client           : Connect Timeout

가능한 원인은 다양하다.

  • Forward Path에서 SYN Drop
  • Firewall의 Silent Drop
  • Server 또는 Network 장애
  • Return Path에서 SYN-ACK Drop
  • Stateful 장비의 비대칭 경로 문제
  • 잘못된 IP 또는 Port

SYN 재전송만으로 어느 지점이 범인인지 확정할 수 없다.

Client, Server, 중간 지점의 Packet Capture를 함께 봐야 한다.


ㅁ RST가 바로 돌아오면 무엇이 다를까?

SYN이 Server Host에 도착했지만 해당 Destination Port에 Listening Socket이 없다고 하자.

Server의 TCP Stack은 일반적으로 RST가 포함된 응답을 보낼 수 있다.

Client → Server : SYN
Server → Client : RST, ACK

 

Client에서는 다음과 같이 즉시 보일 수 있다.

Connection refused

 

Timeout과 비교하면 중요한 단서가 있다.

Timeout : SYN 또는 응답이 보이지 않음 — 여러 Drop 가능성
Refused : TCP 응답이 돌아옴 — Host 도달 후 Listener 부재 가능성

중간 Firewall이 Reject 정책으로 RST를 대신 보낼 수도 있다.

RST가 왔다는 사실만으로 반드시 Server Application이 직접 거절했다고 단정할 수는 없다.

RST Packet의 Source 위치와 TTL, 여러 지점의 Capture를 함께 확인해야 한다.


ㅁ 마지막 ACK가 사라지면 어떻게 될까?

Client가 SYN-ACK을 받고 마지막 ACK를 보냈지만 ACK가 중간에서 손실되었다고 하자.

Client → Server : SYN
Server → Client : SYN-ACK
Client → Server : ACK  손실

Client는 SYN-ACK을 정상적으로 받았으므로 ESTABLISHED로 이동했을 수 있다.

Server는 마지막 ACK를 받지 못해 SYN-RECEIVED에 남는다. Timer가 만료되면 SYN-ACK을 재전송할 수 있다.

Server → Client : SYN-ACK Retransmission
Client → Server : ACK 재응답

Client TCP는 이미 받은 SYN에 대한 재전송임을 알고 ACK를 다시 보낼 수 있다. 일시적 손실이라면 Handshake가 회복될 수 있다.

마지막 ACK 하나가 손실되었다고 연결이 반드시 영구 실패하는 것은 아니다. TCP의 재전송과 상태 처리가 연결 설정에도 적용된다.

Client의 첫 Data Segment가 ACK Flag와 함께 도착하면 Server가 Handshake 완료를 확인하는 데 기여할 수도 있다.


ㅁ Handshake가 끝나면 Application도 준비된 것일까?

TCP Handshake는 Kernel의 TCP Stack 사이에서 완료된다.

Server Process가 Listen하고 있고 Kernel Queue가 연결을 받아들일 수 있으면 Handshake가 완료될 수 있다.

하지만 다음 사실까지 보장하지 않는다.

  • Server Application이 연결을 즉시 accept()했다.
  • Worker Thread가 요청을 처리할 여유가 있다.
  • Database와 외부 API가 정상이다.
  • TLS Handshake가 성공했다.
  • HTTP 요청에 정상 응답할 수 있다.
TCP Connect 성공
≠ TLS 성공
≠ HTTP 성공
≠ Application 업무 처리 성공

TCP 연결 성공은 Transport Layer의 준비가 완료되었다는 뜻이다.

Application Health를 확인하려면 실제 Protocol 요청과 응답까지 관찰해야 한다.


ㅁ SYN이 너무 많이 오면 어떻게 될까?

Server가 SYN을 받으면 완성되지 않은 연결 상태를 일정 시간 유지한다.

공격자가 많은 SYN을 보내고 마지막 ACK를 보내지 않으면 SYN Backlog가 가득 차 정상 Client의 연결을 방해할 수 있다.

이를 SYN Flood 공격이라고 한다.

공격자 → Server : SYN 다수
Server → 공격자 : SYN-ACK
공격자           : 마지막 ACK 없음
Server           : Half-Open Connection 상태 누적

운영체제는 Backlog 크기 조절, SYN-ACK 재시도 제한, Rate Limiting, SYN Cookies 같은 방어 기법을 사용할 수 있다.

SYN Cookies는 초기 SYN 상태의 일부를 Server에 저장하는 대신 Sequence Number에 검증 가능한 정보를 인코딩해 마지막 ACK가 돌아왔을 때 상태를 복원하는 방식이다.

정확한 동작과 사용 조건, Option 처리 영향은 운영체제 구현에 따라 다르다.


ㅁ TCP Handshake와 TLS Handshake는 같은 것일까?

HTTPS 연결에서는 여러 단계가 이어진다.

일반적인 TCP 기반 HTTPS 흐름을 단순화하면 다음과 같다.

1. DNS Lookup
2. TCP 3-Way Handshake
3. TLS Handshake
4. HTTP Request / Response

TCP Handshake는 Transport 연결과 Sequence Space를 준비한다.

TLS Handshake는 암호화 Parameter를 합의하고 인증서를 통해 Server Identity를 확인하며 Session Key를 만든다.

두 Handshake는 계층과 목적이 다르다.

HTTP/3는 QUIC을 사용하므로 TCP 3-Way Handshake 없이 UDP 위의 QUIC/TLS 연결 설정을 수행한다.

“Handshake 실패”라고만 말하지 말고 TCP, TLS, Application 중 어느 단계인지 구분해야 한다.


ㅁ 우편 비유를 네트워크 용어로 바꾸면

지금까지의 장면을 실제 네트워크 용어와 연결하면 다음과 같다.

우편 시스템의 모습 네트워크 용어 의미
연결을 시작하겠다는 첫 등기 SYN Client ISN과 TCP Option을 알리는 연결 요청
요청 확인과 Server 장부 번호 안내 SYN-ACK Client SYN을 ACK하고 Server ISN을 알리는 응답
Server 번호를 받았다는 최종 확인 ACK Server SYN의 수신을 확인해 Handshake 완성
양쪽 발송 장부의 첫 번호 Initial Sequence Number 각 방향 TCP Sequence Space의 시작점
창구가 연결 요청을 기다림 LISTEN Server Socket이 새 연결을 받을 준비 상태
첫 확인 답장을 기다림 SYN-SENT Client가 SYN 전송 후 응답을 기다리는 상태
마지막 확인을 기다림 SYN-RECEIVED Server가 SYN-ACK 후 최종 ACK를 기다리는 상태
양쪽 장부가 동기화됨 ESTABLISHED TCP Data를 주고받을 수 있는 연결 상태
처리 가능한 소포 크기 안내 MSS 상대에게 받을 수 있는 TCP Payload 크기를 광고
미완성 접수 대기 줄 SYN Backlog Handshake가 끝나지 않은 연결 요청을 관리하는 Queue

Sequence Number를 포함한 Handshake는 다음처럼 정리할 수 있다.

Client                                      Server
SYN-SENT                                    LISTEN
  SYN, Seq=1000 -------------------------->
                          <---- SYN-ACK, Seq=5000, Ack=1001
  ACK, Seq=1001, Ack=5001 ---------------->
ESTABLISHED                                 ESTABLISHED

ㅁ 직접 Handshake를 관찰해 보기

Linux에서 TCP Handshake를 Packet Capture로 관찰할 수 있다.

첫 번째 Terminal에서 개인 개발 환경의 TCP Port를 Listen한다. nc 구현에 따라 옵션 문법이 다를 수 있다.

nc -l 8080

두 번째 Terminal에서 접속한다.

nc -v 127.0.0.1 8080

세 번째 Terminal에서 Loopback Traffic을 Capture한다.

sudo tcpdump -n -i lo -vv 'tcp port 8080'

macOS에서는 Loopback Interface가 일반적으로 lo0다.

출력에서 다음 Flag 순서를 찾는다.

Flags [S]   : SYN
Flags [S.]  : SYN-ACK
Flags [.]   : ACK

Wireshark에서는 다음 Display Filter로 SYN이 포함된 Segment를 볼 수 있다.

tcp.flags.syn == 1

TCP State를 운영체제에서 확인한다.

ss -tn state syn-sent
ss -tn state syn-recv
ss -tn state established

정상 Handshake는 매우 빠르게 끝나므로 SYN 상태를 명령으로 포착하기 어려울 수 있다.

Packet Capture가 상태 전이 순서를 확인하는 데 더 적합하다.

공용 Server에 반복 SYN을 보내거나 SYN Flood를 재현하는 행위는 서비스에 영향을 줄 수 있다.

개인 Loopback 또는 허가된 격리 환경에서 정상 연결 한 건만 관찰한다.


ㅁ 핵심 정리

  • TCP 3-Way Handshake는 연결 가능성을 확인하고 양방향 Sequence Space의 시작점을 동기화한다.
  • Client는 SYN에 자신의 ISN을 담고 SYN-SENT 상태로 이동한다.
  • Server는 SYN-ACK으로 Client ISN을 확인하고 자신의 ISN을 알리며 SYN-RECEIVED가 된다.
  • Client의 마지막 ACK가 Server ISN 수신을 확인해야 Server도 ESTABLISHED가 된다.
  • SYN은 Application Data가 없어도 Sequence Number 하나를 소비한다.
  • 실제 ISN은 연결마다 변하며 Capture 도구는 Relative Sequence Number로 표시할 수 있다.
  • SYN Option으로 MSS, Window Scale, SACK Permitted, Timestamp 등을 알릴 수 있다.
  • 응답 없는 SYN은 Timeout을, Listener 없는 Port의 RST는 Refused를 만들 수 있지만 중간 장비도 응답을 생성할 수 있다.
  • 마지막 ACK가 손실되어도 SYN-ACK 재전송과 ACK 재응답으로 회복될 수 있다.
  • TCP Handshake 성공은 TLS와 Application의 정상 동작을 보장하지 않는다.
  • SYN Flood는 Half-Open Connection 상태를 쌓으며 SYN Cookies 등이 완화에 사용될 수 있다.

 

핵심 용어: TCP 3-Way Handshake, SYN, SYN-ACK, ACK, Initial Sequence Number, Sequence Space, Relative Sequence Number, LISTEN, SYN-SENT, SYN-RECEIVED, ESTABLISHED, MSS, Window Scale, SACK Permitted, Timestamp, SYN Backlog, SYN Flood, SYN Cookies, RST

 

세 번째 ACK는 형식적인 마지막 인사가 아니다. Server의 Sequence Number도 Client에게 도착했다는 사실을 확인해 양방향 장부를 완성한다.

 


ㅁ 다음 이야기

Handshake가 끝나 양쪽의 Sequence Space가 준비되었다.

이제 Client가 여러 TCP Segment에 Byte를 나누어 보낸다. 그런데 중간에서 Segment 하나가 사라지면 Sender는 무엇을 보고 손실을 알아챌까?

ACK가 오지 않는 시간과 같은 ACK가 반복되는 현상은 각각 어떤 단서가 될까?

다음 글에서는 「편지가 사라졌다는 사실은 어떻게 알아챌까?」라는 질문을 통해 Cumulative ACK, Retransmission Timeout, Duplicate ACK, Fast Retransmit와 SACK을 살펴본다.

반응형
Comments