서론

이번 글에서는 클라이언트-서버 간 통신을 하기 전 , 서로의 상태를 확인하는 과정인 3-way hadnshake에 대해 알아보고, 

실제 인터페이스의 패킷을 캡쳐해 해당 과정을 확인해보겠습니다.

 

먼저, TCP/IP 프로토콜 스택에서의 4계층은 TCP와 UDP가 담당하고 있습니다.

여기서 4계층의 목적은 목적지를 찾아가는 주소가 아니라 
(이 과정은 이미 2계층-MAC주소과 3계층-IP주소에서 이루어집니다)
 애플리케이션에서 사용하는 프로세스를 정확히 찾아가고 데이터를 분할한 패킷을 잘 분해하고 조립하는 것입니다.

 

TCP 프로토콜은 정보유실 없는 통신을 보장하기 위해 패킷에 번호(Sequence Number)를 부여하고, 잘 전송되었는지에 대해 응답(Acknowledge Number) 합니다. 

 

또한, 한꺼번에 얼마나 보내야 수신자가 잘 받아 처리할 수 있는지 전송 크기(Window Size)까지 고려해 통신합니다.

( TCP/IP 송수신에 대해선 https://yunja.tistory.com/28에 다루었습니다. )

 

본론

TCP에서는 위에서 설명했듯이 유실없는 안전한 통신을 위해 통신 시작 전, 사전 연결작업을 진행합니다.

 

패킷에 순서를 부여하는 것을 Sequence Number, 응답 번호를 부여하는 것을 ACK 번호라고 부른다고 했는데요. 두 번호가 상호작용해 순서가 바뀌거나 중간에 패킷이 손실된 것을 파악할 수 있습니다. 대략적인 과정은 아래와 같습니다.

 

위의 과정으로 HTTP 요청 시, 서버 측의 응답하려는 데이터를 패킷 단위로 쪼개어 보내게 됩니다.

  1. 출발지 송신 측에서 시퀀스 번호를 0으로 보냅니다.
  2. 수신 측에서는 송신측의 0번 패킷을 잘 받았다는 표시로 응답 번호(ACK)에 1을 적어 응답합니다.
  3. 수신 측의 패킷을 받은 송신 측은 시퀀스 번호를 1로 , ACK 번호는 상대방의 0번 시퀀스를 잘 받았다는 의미로 시퀀스 번호를 다시 1로 부여해 송신 합니다.

위의 과정대로 TCP에서의 Sequence Number(Seq), 응답번호 (ACK)가 상호작용하여 데이터를 주고 받는 것을 살펴보았는데요.

 

이것을 토대로, 오늘의 주제인 위 과정이 실행되기 전의 사전 연결작업인 3방향 핸드셰이크에 대해 알아보겠습니다.

패킷 네트워크에서는 동시에 많은 상대방과 통신하므로 정확한 통신을 위해서 각 통신에 필요한 리소스를 미리 확보하는 작업이 중요합니다.

TCP에서는 3번의 패킷을 주고받음으로써 통신을 서로 준비(송,수신 측이 현재 패킷을 주고 받을 수 있는 상태인지)하기 때문에 3방향 핸드셰이크라고 부릅니다.

 

 

  1. 통신 시도시 송신자는 플래그에 있는 SYN 필드를 1로 표기해 패킷을 보냄
    • 이때 자신이 사용할 첫 seq no(시퀀스 번호)를 적어 보냄.
  2. SYN 패킷을 받은 수신자는 SYN과 ACK 비트를 플래그에 1로 표기해 응답함.
    • 첫 패킷이므로 SYN을 1로 표기하고, 송신자가 보냈던 패킷의 응답이기도 하니, ACK 도 함께 1로 써서 보냄
  3. ACK 번호는 10번까지 잘 받았으니 다음에는 10+1번을 달라는 의미임.
  4. 수신자의 응답을 받은 송신자는 연결 확립을 위해 다시 한번 응답 메시지를 보냄.
    • 수신자가 ACK 번호를 11로 표기했으니, seq no 를 11로 표기해 응답함.
    • 동시에 수신자의 시퀀스 번호 20에대한 응답이니, ACK번호를 2로 보냄(20번까지 잘 받았음, 다음꺼 보내줘)
    • ACK 번호를 비교해가며, 패킷 유실이 있으면 파악 후, 메모리에 유지해놓은 데이터를 이용해 재전송함.

대략적인 과정을 통해 3방향 핸드 셰이크 패킷이 어떻게 주고 받는지 알아봤으니, 직접 패킷을 캡쳐해 확인해보도록 하겠습니다.

curl www.google.com을 통해 GET 요청을 보낸 후, 캡쳐한 패킷 (*와이어샤크)

 

$  sudo tcpdump -i en0 -w /Users/yunsmac/Desktop/google.pcap tcp port 80

위 명령은 현재 퍼블릭 네트워크로 통신 중인 제 MAC 주소를 가진 인터페이스에 대해, tcp프로토콜 port 80인 패킷만 캡쳐해 제 로컬에 저장하겠다는 의미입니다. HTTP 요청시 사용하는 포트번호는 기본적으로 80이며, 이는 Well known 포트라고 합니다.
  1. 첫 번째 줄의 Source(출발지 주소)는 제 ip 주소, Destination(목적지 주소)는 google.com의 IP 주소입니다.
    1. 제가 클라이언트의 입장으로 서버에서 데이터를 받아오기 위해 연결을 시작한다는 의미이죠.
    2. 송신측에선 첫 번째 패킷이니 SYN 플래그를 1, 시퀀스 번호를 0으로 사용해 패킷을 전송합니다.
  2. 두 번째 줄에서 출발지 ip주소와 목적지 ip주소가 변경되어 google 서버에서 SYN, ACK 플래그로 응답해주는 모습입니다.
    1. 이 때, 수신측에서 보내주는 첫 패킷이니 시퀀스 번호는 0, 응답 번호는 수신 측의 시퀀스 번호에 1을 더해 보내줍니다.
    2. 수신측의 응답을 잘 받았으니 송신측은 송신 측의 Ack 번호를 시퀀스 번호로 설정하고, 응답 번호는 수신 측의 시퀀스 번호에 1을 더합니다.
  3. 네 번째 줄에서 서로 간 통신을 하기 위한 사전 연결 작업이 끝났으니, 송신 측에서는 그제서야 HTTP GET 요청을 보냅니다.

  4. 5 ~ 15번째 줄에선 서버 측에서 요청에 대한 응답 데이터를 보내는 과정입니다.

  5. 16 ~ 17 번째 줄은 클라이언트 측의 Window Size( 현재 받을 수 있는 패킷의 양)을 보내줍니다- [TCP Window Update].
    1. 이후 클라이언트 측의 윈도우 사이즈를 확인한 서버 측은 클라이언트 측이 윈도우 사이즈가 넉넉하니, 계속해서 데이터를 전송합니다.
    2. 이때, 서버 측은 패킷을 전송할 때, TCP 헤더에 PSH 플래그를 설정해 보냅니다.
    3. PSH 플래그는 서버 측에서 전송할 데이터가 없거나 데이터를 버퍼링 없이 응용 프로그램으로 즉시 전달할 것을 지시할 때 사용됩니다.
    4. 또한, HTTP status 200 으로 성공적으로 응답이 완료되었다는 패킷도 함께 전송합니다.
  6. 27 ~ 31 번째 줄 . 모든 패킷이 잘 분해되고 조립되어 클라이언트 - 서버 측 간 연결을 종료하게 됩니다. (4방향 핸드셰이크)  
    1. 이때는 FIN 플래그를 사용하게 됩니다.
    2. FIN 플래그는 연결 종료 시 1로 표시되며, 위와 같이 데이터 전송을 마친 후 정상적으로 양방향 종료 시 사용됩니다.

 

결론 및 참고

이로써, 클라이언트 - 서버 간 통신 상황에서 3방향 핸드셰이크가 어떤 과정으로 이루어지는 지 직접 확인해보았습니다.

 

참고로 정상적으로 양방향 종료 시, FIN 플래그가 1로 표시 되어 사용된다고 했는데 실제 서비스를 운영하는 과정에서 서버 측에서 프로세스가 종료 되었거나 의도치 않는 동작을 했을 경우, 서버 측에서 보낸 패킷에서 RST 플래그가 표시 될 수도 있습니다. 

 

RST 플래그 또한 연결 종료 시 1로 표시되어 사용되지만, FIN 플래그와 달리 연결 강제 종료를 위해 연결을 일방적으로 끊을 때 사용됩니다.(서버 측이던, 클라이언트 측이던 무엇인가 문제가 생겼다는 뜻이겠죠 ? )

 

이번 글에서는 3방향 핸드 셰이크 과정을 보기 위해 해당 과정 패킷을 중점적으로 보았지만,

 

이런식으로 패킷을 캡쳐하여 어떤 부분에서 문제가 일어났는지 분석을 할 수 있다면 , 서비스 운영 및 여러 문제 해결에 도움을 더 줄 수 있을 것 같습니다. 끝으로 지인 분께서 저한테 해주셨던 말씀으로 이 글을 마치도록 하겠습니다.

패킷은 절대 거짓말을 하지 않는다.
- 어느 훌륭한 네트워크 엔지니어(CCIE) - 

'Network' 카테고리의 다른 글

TCP 송/수신 원리  (8) 2023.09.03
DHCP ? (Dynamic Host Configuration Protocol)  (5) 2023.08.09

+ Recent posts