소켓을 생성한 뒤 소켓사이를 연결하는 커넥션을 만들고 데이터 송/수신을 진행한 뒤 연결을 끊는 과정까지 모두 공부해 보았다.
이제 배울 것은 실제 송/수신되는 데이터인 패킷이 어떻게 만들어지는가와 어떻게 송/수신되는가에 대해 배울 차례이다.
하지만 이를 배우기 전 데이터/송신 과정 정체를 정리해 보고 이전에 말했던 오류 검출 및 회복에 대해 알아본 뒤 패킷에 대해 알아보도록 하자.
일단 서버 측에서는 애플리케이션을 동작시키면서 소켓을 미리 만든 뒤 접속 대기 상태로 만든다.
이때 소켓을 "Wait for Client 상태"에 있다고 한다.
이후 Client는 데이터 송/수신을 위하여 소켓을 만든다.
그리고 SYN 비트가 1인 TCP 헤더를 만들고 클라이언트 측에서 사용하는 시퀀스 번호 초기값을 기록하여 서버 측에 보냄으로써 접속 동작을 실행한다.
이 때 소켓을 "SYN_SENT 상태"에 있다고 한다.
서버 측은 클라이언트에서 보낸 패킷을 받을 것이며 서버 측 소켓 상태를 "SYN_RECEIVED 상태"로 변경한다.
이후 서버 측 메모리에 클라이언트 시퀀스 번호 초기값을 저장한다.
이젠 서버가 클라이언트 측에게 응답 메시지를 보낸다.
이때 SYN 비트를 1로 설정하고 서버 측의 시퀀스 번호 초기값을 기록한다.
또한 ACK 비트를 1로 설정하여 ACK 번호 영역이 유효함을 나타낸 뒤 ACK 번호를 기록하여 클라이언트 측에서 보낸 패킷이 정상적으로 도착했음을 알린다.
클라이언트가 서버에서 보낸 응답 메시지를 받았다면 클라이언트 측 메모리에 서버 시퀀스 번호 초기값을 저장한다.
이후 ACK 비트를 1로 설정하여 응답 메시지를 보냄으로써 서버 측에도 서버 측에서 보낸 초기 시퀀스 번호 값을 잘 받았음을 알린다.
마지막으로 서버가 브라우저에서 보낸 응답 메시지를 받으면 세션과 커넥션을 생성함으로써 서버 측 소켓은 Established(연결 완료) 상태가 된다.
이제 데이터는 커넥션을 통해 송/수신된 다음 세션을 통해 브라우저에게 전달될 수 있는 것이다.
이처럼 연결 동작은 3단계를 거쳐 메시지를 주고받음으로써 클라이언트와 서버 모두 데이터를 송/수신할 준비가 되었다는 것을 안전하게 보장한다.
이 모든 과정을 "3-way Handshake"라고도 한다.
데이터 송/수신 동작은 애플리케이션 종류에 따라 다르지만 HTTP 1.0을 사용하는 웹의 경우 클라이언트에서 Request Message를 보내는 것부터 시작한다.
프로토콜 스택의 TCP 담당은 MTU를 고려하여 Request Message를 적당한 크기로 분할한 뒤 TCP 헤더를 맨 앞에 붙인다.
TCP 헤더에는 패킷에 담긴 데이터가 몇 번째 바이트부터 시작되는 데이터 조각인지를 나타내주는 시퀀스 번호와 데이터 길이 계산을 위해 헤더 길이를 기록해 놓는다.
이후 IP 담당이 MAC 헤더 및 IP 헤더를 붙여 패킷으로 만들고 서버 측에 송신한다.
서버 측에선 이 패킷을 받아 데이터를 추출한다.
또한 서버는 클라이언트 측에 데이터를 잘 받았다는 표시로 ACK 번호를 저장한 패킷을 보내는데 ACK 번호로는 (지금까지 받았던 데이터 길이 + 1)만큼의 값으로 설정하여 보낸다.
이후 서버 측에선 클라이언트 측의 요청에 따라 결과물을 낼 것이고 이 결과물을 클라이언트 측에 보낼 것이다.
이 과정은 정확히 데이터 송신 과정에서 웹 서버와 클라이언트 입장만 바뀔 뿐 똑같은 동작이 수행된다.
(HTTP 1.0을 사용하는 웹 통신의 경우) 서버 측에서 요청 메시지(클라이언트 요청에 대한 결과물)를 보내면 서버 측은 더 이상 보낼 데이터가 없다는 것을 파악하고 close 명령어를 통해 연결 끊기 동작에 들어간다.
먼저 서버는 연결을 끊기 위하여 컨트롤 비트 중 FIN 비트를 1로 설정한 뒤 클라이언트에게 TCP 헤더를 보낸다.
이때 서버 소켓은 "FIN_WAIT 상태"에 들어간다.
클라이언트가 FIN 비트가 1로 설정된 패킷을 받았다면 먼저 ACK 비트를 1로 설정한 TCP 헤더를 서버 측에 보낸다.
즉, ACK 번호를 기록하여 서버 측에 응답 메시지를 보내 패킷이 정상적으로 도착했음을 알린다.
이후 클라이언트는 수신 버퍼 메모리 영역에 남아있는 데이터를 모두 애플리케이션 측에 보낸 뒤 서버 측에서 연결을 끊었다는 사실을 통지한다.
클라이언트 애플리케이션(브라우저)은 서버 측에서 연결을 끊었음을 인지하고 close 명령을 수행하여 명령 끊기 동작에 들어간다.
이 때 FIN 비트를 1로 설정한 뒤 서버에게 TCP 헤더를 보내며 클라이언트 측 소켓은 "LAST_ACK 상태"로 변한다.
서버는 FIN 1비트가 1로 설정된 패킷을 받을 것이며 이에 대한 응답으로 ACK 비트를 1로 설정해 TCP 헤더를 클라이언트 측으로 보낸다.
이후 서버 소켓은 "FIN_WAIT 상태"에서 "TIME_WAIT 상태"로 변경된다.
이 때 서버 소켓과 클라이언트 소켓은 바로 소멸되지 않는데, 혹시 연결 끊기 동작을 위해 보낸 패킷이 유실되어 문제가 발생하는 것을 방지하기 위해서이다.
또한 연결 끊기 동작을 위한 TCP 헤더보다 늦게 도착하는 잉여 패킷을 기다리는 목적도 있다.
연결 끊기 동작은 이렇게 총 4단계를 거쳐 진행되어 연결을 안전하게 종료시킨다.
이 모든 과정을 "4-way Handshake"라고도 한다.
추가로 연결은 끊기더라도 서버 측에 저장되어 있는 세션은 소멸되지 않는다.
그리고 이전에 해당 세션을 사용했던 클라이언트 측에서 다시 접속을 요청할 경우 새로운 세션을 만드는 것이 아닌 기존에 사용했던 세션에 연결시킴으로써 세션에 저장되어 있던 정보를 계속해서 활용할 수 있다.
단, 세션이 영구적으로 지속될 경우 서버 측의 부담으로 다가올 수 있기 때문에 시간을 정하여 지정한 시간 내에 클라이언트 측에서 연결 요청이 오지 않을 경우 Session Timeout을 통해 세션을 소멸시킨다.