1. 라이브러리
  2. TCP와 UDP
  3. TCP 심층 분석

업데이트됨 1개월 전

모든 TCP 연결은 하나의 문제에서 시작됩니다. 처음 만나는 두 기기가 안정적으로 통신하기 전에 공통된 시작점에 합의해야 한다는 것입니다. 두 기기 사이의 인터넷은 적대적인 환경입니다—패킷은 유실되고, 중복되고, 순서가 바뀌고, 지연됩니다. 신뢰할 수 없는 환경에서 어떻게 신뢰를 구축할 수 있을까요?

바로 3방향 핸드셰이크입니다.

왜 세 단계가 필요한가

TCP는 데이터가 순서대로, 누락 없이, 오류가 감지된 상태로 도착하는 것을 보장합니다. 이 약속을 이행하기 위해 TCP는 모든 바이트에 시퀀스 번호를 부여합니다. 패킷이 순서 없이 도착하면 수신자가 재정렬합니다. 패킷이 사라지면 수신자는 어떤 바이트를 다시 요청해야 하는지 정확히 알 수 있습니다.

하지만 시퀀스 번호는 양쪽이 어디서부터 번호를 셀지 합의한 경우에만 작동합니다. 클라이언트는 시퀀스가 100에서 시작한다고 생각하는데 서버는 5000에서 시작한다고 생각한다면, 무엇이 수신되었는지 절대 합의할 수 없습니다.

핸드셰이크는 이 시작점들을 동기화합니다. 세 번의 교환이 최소값입니다—관례가 아니라 논리적으로 그렇습니다. 양쪽 모두 시작 번호를 보내고 상대방의 번호를 수신했음을 확인해야 합니다. 두 단계로는 그게 불가능합니다.

세 단계

1단계: SYN (동기화)

클라이언트가 SYN 플래그가 설정된 패킷을 전송합니다: "연결하고 싶습니다. 내 시퀀스 번호는 X부터 시작합니다."

시작 번호(초기 시퀀스 번호, ISN)는 무작위로 선택됩니다. 왜일까요? 이전 연결의 오래된 패킷이 아직 인터넷을 떠돌고 있을 수 있습니다. 모든 연결이 0에서 시작한다면, 이전 연결에서 지연된 패킷이 새 연결의 데이터로 오인될 수 있습니다. 무작위 시작점은 이런 충돌이 일어날 가능성을 천문학적으로 낮춥니다.

SYN 패킷에는 클라이언트의 최대 세그먼트 크기와 윈도우 크기(버퍼링할 수 있는 데이터 양)도 포함됩니다.

2단계: SYN-ACK (동기화-확인)

서버는 두 가지를 동시에 수행하는 패킷으로 응답합니다:

  1. 클라이언트의 SYN 확인: "당신의 시작 번호 X를 수신했습니다. 이제 X+1을 기다립니다."
  2. 서버의 SYN 전송: "내 시퀀스 번호는 Y부터 시작합니다."

이 두 가지를 하나의 패킷으로 합치기 때문에 4방향이 아닌 3방향 핸드셰이크입니다. 서버가 확인 패킷과 동기화 패킷을 따로 보낼 수도 있지만, 합치면 왕복 하나를 절약할 수 있습니다.

3단계: ACK (확인)

클라이언트는 서버의 시퀀스 번호를 확인하여 핸드셰이크를 완료합니다: "당신의 시작 번호 Y를 수신했습니다. 이제 Y+1을 기다립니다."

이제 양쪽이 동기화되었습니다. 데이터 전송을 시작할 수 있습니다.

핸드셰이크의 모습

클라이언트                               서버
   |                                       |
   |  SYN (seq=100)                        |
   |--------------------------------------→|
   |                                       |
   |         SYN-ACK (seq=300, ack=101)    |
   |←--------------------------------------|
   |                                       |
   |  ACK (seq=101, ack=301)               |
   |--------------------------------------→|
   |                                       |
   |  연결 수립                             |

확인 번호는 항상 "상대방의 시퀀스 번호 더하기 1"입니다—"그 시점까지 모든 것을 수신했으니, 다음 바이트를 보내세요."라는 의미입니다.

핸드셰이크가 실패할 때

수신 대기 중인 것이 없는 경우. 요청된 포트에 바인딩된 애플리케이션이 없으면 서버는 RST(재설정) 패킷을 전송합니다: "여기엔 아무것도 없습니다. 돌아가세요." 연결 시도는 즉시 종료됩니다.

SYN이 유실된 경우. 클라이언트가 SYN을 전송했지만 도착하지 않습니다. 타임아웃 후 클라이언트가 재시도합니다. 시스템은 포기하기 전에 대기 시간을 점점 늘려가며 여러 번 재시도합니다. 연결 타임아웃이 30초 이상 걸릴 수 있는 이유가 여기 있습니다—시스템이 불안정한 네트워크에 인내심을 발휘하고 있는 것입니다.

방화벽이 패킷을 아무 응답 없이 차단하는 경우. 클라이언트 입장에서는 패킷 손실과 구분이 되지 않습니다. SYN이 허공으로 사라집니다. 아무 응답도 오지 않습니다. 클라이언트는 기다리고, 재시도하고, 더 오래 기다리고, 다시 재시도하다가 결국 포기합니다. 일부 방화벽은 RST 패킷을 전송하여 최소한 빠르게 실패하도록 합니다.

핸드셰이크 도중 클라이언트가 사라지는 경우. 서버가 SYN을 수신하고 SYN-ACK로 응답한 뒤 ACK를 기다립니다. 그런데 클라이언트가 갑자기 다운되거나 연결을 잃었습니다. 서버는 절대 완료되지 않을 연결을 위해 리소스를 할당한 채로 대기합니다. TCP의 keepalive 메커니즘이 결국 이런 반쪽짜리 연결을 정리하지만, 시간이 걸립니다.

SYN 플러드 공격

핸드셰이크에는 취약점이 있으며, 공격자들은 이를 무자비하게 악용합니다.

서버가 SYN을 수신하면 대기 중인 연결을 추적하기 위해 메모리를 할당합니다—클라이언트의 시퀀스 번호, 포트 및 기타 상태를 저장합니다. 핸드셰이크가 완료되기 전에 이 작업을 수행하는데, SYN-ACK를 전송하려면 클라이언트의 정보를 기억하고 있어야 하기 때문입니다.

공격자는 위조된 출처 주소로 수천 개의 SYN 패킷을 보냅니다. 서버는 각각에 성실하게 SYN-ACK로 응답하며, 절대 완료되지 않을 연결을 위해 리소스를 할당합니다. 확인 패킷은 존재하지 않거나 연결을 요청하지 않은 위조 주소로 날아갑니다. 서버는 영원히 도착하지 않을 최종 ACK를 기다립니다.

서버의 연결 큐가 가득 찹니다. 정상적인 사용자들이 연결을 시도해도—자리가 없습니다. 서버는 트래픽 때문이 아니라 자신의 친절함 때문에 무너집니다—문을 두드리는 모든 낯선 이를 위해 리소스를 할당하다가 말입니다.

방어책은 영리합니다. SYN 쿠키를 사용하면 서버가 최종 ACK가 도착할 때까지 아무것도 저장하지 않아도 됩니다. 클라이언트의 정보를 기억하는 대신, 서버는 그 정보를 암호학적으로 자신이 돌려보내는 시퀀스 번호에 담습니다. 클라이언트가 ACK를 가지고 돌아오면, 서버는 확인 번호에서 원래 연결 정보를 복원합니다. ACK가 끝내 오지 않는다면 서버는 아무것도 잃지 않습니다—처음부터 리소스를 할당하지 않았으니까요.

서버는 방문자를 기억하는 것을 멈추고, 대신 입장권을 그들의 이마에 새겨 보냅니다. 유효한 입장권을 가지고 돌아오는 자만 안으로 들어올 수 있습니다.

TCP 3방향 핸드셰이크에 관한 자주 묻는 질문

TCP가 세 단계 대신 두 단계를 사용할 수 없는 이유는 무엇인가요?

SYN과 SYN-ACK 이후, 서버는 클라이언트가 자신의 시퀀스 번호를 받았다는 증거가 없습니다. 허공에 정보를 보낸 셈입니다. 세 번째 단계—클라이언트의 ACK—가 서버의 시퀀스 번호가 제대로 전달되었음을 확인합니다. 이 확인 없이는 서버가 아직 동기화되지 않았을 수 있는 클라이언트에게 데이터를 전송하기 시작할 것입니다.

지연이 큰 연결에서 핸드셰이크는 어떻게 되나요?

각 단계가 네트워크를 가로질러야 하므로, 지연이 크면 연결 수립이 느려집니다. 왕복 시간이 600ms인 위성 링크는 어떤 데이터도 전송되기 전에 핸드셰이크만으로도 거의 2초가 걸립니다. 이것이 QUIC 같은 프로토콜이 연결 수립과 암호화 설정을 결합하여 왕복 횟수를 줄이는 이유입니다.

시퀀스 번호가 32비트인 이유는 무엇인가요?

32비트 시퀀스 번호는 순환하기 전에 약 40억 바이트를 셀 수 있습니다. 느린 연결에서는 이것으로 충분합니다—오래된 패킷은 순환 전에 이미 만료됩니다. 매우 빠른 연결(10Gbps 이상)에서는 TCP가 PAWS(래핑된 시퀀스 보호)를 사용하여 타임스탬프를 추가함으로써 같은 시퀀스 번호를 가진 이전 패킷과 새 패킷을 구분합니다.

3방향 핸드셰이크가 실제로 일어나는 것을 볼 수 있나요?

네. Wireshark 같은 도구는 네트워크 패킷을 캡처하여 각 단계를 보여줍니다. 첫 번째 패킷에서 SYN 플래그, 두 번째 패킷에서 SYN+ACK, 세 번째에서 ACK를 볼 수 있습니다. TCP가 실제로 회선에서 무엇을 하는지 이해하는 가장 명확한 방법 중 하나입니다.

이 페이지가 도움이 되었나요?

😔
🤨
😃