[TCP/IP] 브라우저와 프로토콜 스택이 데이터를 송수신하는 과정
브라우저에 url을 입력하면 일어나는 일
브라우저에 url을 입력하면 가장 가까운 DNS 서버에 IP 주소를 요청한다.
DNS 서버에 조회한다는 것은 DNS 서버에 조회 메시지를 보내고, 응답 메시지를 받는다는 것을 의미한다.
브라우저 자체에는 메시지를 네트워크로 송출하는 기능이 없으므로 Socket 라이브러리를 사용하여 OS의 프로토콜 스택을 호출한다.
프로토콜 스택
프로토콜 스택에는 TCP/UDP 프로토콜을 사용하여 데이터 송수신을 담당하는 부분과 IP 프로토콜을 사용하여 패킷 송수신 동작을 제어하는 부분이 있다.
프로토콜 스택은 내부에 제어 정보를 기록하는 메모리 영역(소켓)을 가지고 있으며, 여기에 통신 동작을 제어하기 위한 제어 정보를 기록한다.
프로토콜 스택은 소켓에 기록된 제어 정보를 참조하면서 동작한다.
Socket과 리졸버
Socket 라이브러리는 OS의 프로토콜 스택을 브라우저 등의 애플리케이션에서 호출하기 위한 프로그램 라이브러리이다.
Socket 라이브러리는 데이터를 송/수신하는 컴퓨터 사이에 파이프와 같은 통로를 연결하여 양방향으로 데이터를 전송한다.
DNS 리졸버는 Socket 라이브러리의 부품화된 프로그램 중 하나이며, DNS 서버에 요청 메시지를 보내고 응답 메시지를 받아 애플리케이션(브라우저)으로 전달하는 역할을 한다.
gethostbyname(리졸브의 프로그램명)과 웹 서버의 이름(www.lab.cyber.co.kr)으로 리졸버를 호출한다.
리졸버 내부의 작동
- 리졸버를 호출하면 제어가 리졸버의 내부로 넘어간다. (②) (브라우저에서 실행중인 동작이 일시정지 되고(①), 리졸버 코드로 넘어간다)
- 리졸버가 DNS 서버에 문의하기 위한 메시지를 만들어 OS의 프로토콜 스택에 실행을 의뢰한다. (③)
- LAN 어댑터를 통해 메시지가 DNS 서버로 송신된다. (④,⑤)
- DNS 서버가 응답 메시지를 클라이언트에게 반송한다. (⑥)
- 메시지가 네트워크와 프로토콜 스택을 경유하여 리졸브에게 전달된다. (⑦,⑧)
- 리졸버가 내용을 해독하고 애플리케이션에 IP 주소를 건내준다. (⑨)
- 리졸버의 동작이 끝나고 제어가 애플리케이션(브라우저)에게 돌아온다. (⑩)
브라우저의 데이터 송/수신
웹 서버에 메시지를 송/수신할 때도 리졸버를 통해 DNS 서버를 조회한 것과 마찬가지로 OS 내부에 있는 프로토콜 스택에 의뢰한다.
프로토콜 스택에 메시지 송신 동작을 의뢰할 떄는 Socket 라이브러리 프로그램 부품을 결정된 순번대로 호출한다.
Socket의 송/수신 단계
소켓의 송/수신 단계에는 크게 작성, 접속, 송/수신, 말소 단계가 있다.
작성 (①)
- socket을 호출하여 프로토콜 스택에 소켓을 만들것을 의뢰한다.
- 프로토콜 스택에서 소켓의 제어 정보를 담을 메모리 영역을 확보한다.
- 송/수신 동작이 시작되기 전 초기 상태임을 나타내는 제어 정보를 소켓의 메모리 영역에 기록한다.
- 소켓이 만들어지면 소켓을 식별하기 위한 디스크립터를 애플리케이션에 알려준다.
접속 (②)
- 클라이언트는 connect를 호출하여 프로토콜 스택의 TCP 담당 부분에 소켓 제어 정보를 전달한다.
- TCP 담당 부분은 TCP 헤더를 만들어 제어 정보를 기록하고 TCP 헤더의 SYN를 1로 만든다.
- TCP 헤더의 송신처와 수신처의 포트 번호로 클라이언트측 소켓과 서버측 소켓을 지정한다.
- TCP 헤더 정보를 IP 담당 부분에 전달하면 IP 담당 부분이 서버에 패킷을 송신한다.
- 서버는 패킷을 받았다는 것을 알리는 의미로 TCP 헤더의 ACK를 1로 만든다.
- 서버가 패킷을 응답하면 클라이언트측 IP 담당 부분에서 수신하여 다시 TCP 담당 부분에 전달한다.
- 클라이언트가 TCP 헤더를 조사하여 서버측 접속 동작이 성공인지 체크하고, 성공이면 패킷이 잘 도착했다는 것을 서버에 알리기 위해 ACK 비트를 1로 만든 TCP 헤더를 반송한다.
- 소켓이 데이터를 송/수신할 수 있는 상태가 되어 소켓이 파이프로 연결(커넥션)되었다고 할 수 있다.
이 과정이 3 way handshake 이다.
송신 (③)
- write를 호출하여 사용자가 입력한 URL을 바탕으로 만든 HTTP 리퀘스트 메시지를 프로토콜 스택에 전달한다.
- 프로토콜 스택은 받은 데이터를 일단 내부의 송신용 버퍼 메모리 영역에 저장한다. 송신 시점은 아래와 같다.
- OS의 종류나 버전에 따라 MSS(하나의 패킷으로 운반할 수 있는 데이터의 최대 길이)를 초과하거나 비슷한 길이까지 데이터를 저장했을 때, 혹은 내부 타이머로 일정 시간 이상 경과했을 때 패킷을 송신한다.
- 애플리케이션 쪽에서 송신 타이밍을 제어할 수도 있는데, 프로토콜 스택에 송신을 의뢰할 때 버퍼에 저장하지 않고 바로 송신하도록 옵션을 지정할 수 있다.
- 긴 데이터를 송신할 경우 MSS의 크기에 맞게 쪼개서 패킷에 넣어 송신한다. 이 때 시퀀스 번호(쪼갠 조각이 몇 번째 바이트인지)를 TCP 헤더에 기록한다.
- IP 담당 부분에 데이터를 건네주어 송신 동작을 실행한다.
- 수신측에서 스퀀스 번호를 바탕으로 누락이 없는 것을 확인하면 데이터를 몇 번째 바이트까지 수신한 것인지 계산하고, TCP 헤더의 ACK 번호에 기록하여 송신측에 알려준다.
- 만약 상대측에서 ACK 번호가 돌아오지 않으면 TCP는 패킷을 다시 전송한다. 패킷을 몇 번 다시 보내도 응답이 없으면 데이터 송신 동작을 강제로 종료하고 애플리케이션에 오류를 통지한다.
수신 (③)
- 브라우저가 HTTP 리퀘스트 메시지를 서버에 송신 완료하면, 서버로부터 응답 메시지를 받기 위해 read를 호출한다.
- 수신 과정은 송신과 마찬가지로 진행된다. 다만 이번에는 서버에서 클라이언트로 스퀀스 번호와 데이터를, 클라이언트에서 서버로 ACK 번호를 송신한다.
- 수신 데이터를 수신 버터에 임시 보관하고, 조각을 연결하여 데이터를 복원한 후 애플리케이션에 전달한다.
연결 끊기 (④)
- 데이터를 송신하는 측에서 송신이 전부 완료되면 close를 호출하여 연결 끊기 단계에 들어간다.
- 송신측에서 TCP 헤더에 연결 끊기를 나타내는 FIN에 1을 기록하여 상대측에 전송한다.
- 수신측에서 패킷을 받으면 송신측으로 ACK 번호를 반송한다.
- 수신측에서 애플리케이션에 수신 완료를 알리고 애플리케이션에서 close를 호출한다.
- 수신측 프로토콜 스택도 FIN에 1을 기록하여 상대측에 전송한다.
- 상대측에서 ACK 번호가 돌아오면 대화가 끝난다.
- 패킷 전송에 실패한 경우 다시 보내는 동작이 있을 수 있기 때문에 소켓은 바로 말소하지 않고, 몇 분 정도 기다린 후 말소한다.