01. 웹 브라우저가 메시지를 만든다. (웹 브라우저의 내부탐험) - chwyym/Network GitHub Wiki

1. HTTP Request 메시지를 작성한다.

1) 탐험 여행은 URL 입력부터 시작한다.

URL의 맨 앞에는 액세스하는 방법을 적는데, 여기에는 http:, ftp:, file:, mailto: 와 같은 프로토콜들이 해당된다. 참고로, 액세스 대상이 웹 서버라면 HTTP라는 프로토콜을 사용하여 액세스하고, FTP 서버라면 FTP라는 프로토콜을 사용하는 식이다.

2) 브라우저는 먼저 URL을 해독한다.

브라우저는 맨 처음에 http://웹서버명/디렉토리명/.../파일명http:+//+웹서버명+/+디렉토리명+/+...+파일명으로 분해시킨다.

3) HTTP의 기본 개념

HTTP 프로토콜은 클라이언트와 서버가 주고받는 메시지의 내용이나 순서를 정한것으로, 클라이언트에서 서버를 향해 Request Message를 보내면 서버에서는 Status code를 리턴하게 된다. 클라이언트가 보내는 Request Message는 '무엇을','어떻게'에 대한 내용이 들어있는데, '무엇을'에 해당하는 것이 URI이고, '어떻게'에 해당하는 것이 '메서드'이다.

[그림1] HTTP의 기본개념

4) HTTP Request Message를 만든다.

URL을 해독하고 웹 서버와 파일명을 판단하면 브라우저는 이것을 바탕으로 HTTP의 리퀘스트 메시지를 만든다. 실제 HTTP 메시지는 쓰는 방법, 즉 포맷이 결정되어 있으므로 브라우저는 이 포맷에 맞게 Request Message를 만든다.

  • Request Message의 첫 번째 행은 Request Line이며, 두 번째행 부터는 Message Header가 나오고 그 뒤에 메시지 본문이 나온다.
  • Response Message의 첫번째 행은 Status Line이며, 두 번째행 부터는 Message Header가 나오고 그 뒤에 메시지 본문이 나온다.

[그림2] HTTP Request


[그림3] HTTP Response


[그림4] Status Code 전체 구조


[그림5] Status Code 세부 내용


'메시지 헤더'에는 '제네럴 헤더', '리퀘스트 헤더', '응답 헤더' 그리고 '엔티티 헤더' 로 나누어진다. 그리고 마지막 '메시지 본문'에는 메시지의 실제 내용이 들어간다.

2. 웹 서버의 IP 주소를 DNS 서버에 조회한다.

1) IP 주소의 기본

HTTP Message를 만들면 다음에는 이것을 OS에 의뢰하여 액세스 대상의 웹 서버에게 송신한다. 이때 URL안에 쓰여있는 서버의 도메인명에서 IP 주소를 조사해야하며, 네트워크 번호+호스트 번호를 IP 주소라고한다. (참고로, IP에 Net masking을 하면 Host number가 나온다.)

2) Socket Library가 IP 주소를 찾는 기능을 제공한다.

DNS 클라이언트에 해당하는 것(DNS서버에 실제 IP를 찾기 위해 요청하는 것)을 DNS Resolver 또는 단순히 Resolver라고 부른다.

{
 ...
 <메모리 영역> = gethostbyname("www.lab.cyber.co.kr");
 ...
 <HTTP 메시지 송신>
 ...
{

위의 그림에서 gethostbyname이 리졸버의 프로그램명이다. 그리고 이 명령을 통해 Domain의 실제 IP 주소를 알 수 있다.

3) 리졸버 내부의 작동

메시지 송신동작은 리졸버가 스스로 실행하는 것이 아니라 OS 내부에 포함된 Protocol Stack을 호출하여 실행을 의뢰한다.

// Application
{
 ...
 <메모리 영역> = gethostbyname("www.abc.co.kr");
 ...
}

// Socket
gethostbyname {
 DNS 서버에 보내는 조회 메시지를 만든다;
  조회 메시지를 DNS 서버에 보낸다;
 DNS 서버에서 돌아온 응답 메시지를 수신한다; 
 응답 메시지에서 IP 주소를 추출하여 <메모리 영역> 저장하고 애플리케이션으로 돌아간다;
}

// OS 내부의 프로토콜 스택
UDP 메시지 송신 {
 송신 동작
}
UDP 메시지 수신 {
 수신 동작
}



3. 전 세계의 DNS 서버가 연대한다.

1) DNS 서버의 기본 동작

DNS 서버의 기본 동작은 클라이언트에서 조회 메시지를 받고 조회의 내용에 응답하는 형태로 정보를 회답하는 일이다.

"DNS 서버는 서버에 등록된 도메인명과 IP 주소의 대응표를 조사하여 IP 주소를 회답한다."

(등록 정보는 설정 파일 등에 입력되어 있다. 이것을 리소스 레코드라고 한다.)

조회 메시지 구성

조회 메시지에는 세 가지 정보가 포함되어 있다.

  • 이름
    • 서버나 메일 배송 목적지와 같은 이름
  • 클래스
    • 네트워크를 식별하기 위해 준비됨.
    • 그러나 현재는 인터넷 이외의 네트워크는 소멸되었으므로 (IN 값 고정)
  • 타입
    • 이름에 어떤 타입의 정보가 지원되는지를 나타냄
    • 타입 A - 이름이 IP 주소가 지원됨
    • 타입 MX - 이름에 메일 배송 목적지가 지원됨
    • 이외에 여러가지 타입이 존재함
예시
이름 = www.lab.cyber.co.kr
클래스 = IN
타입 = A

위 메시지를 DNS에 보내면 위의 세 가지가 일치하는 정보를 찾아서 보내준다.


이름 = cyber.co.kr
클래스 = IN
타입 = MX

[email protected]이라는 메일 주소가 있는데, 배송 목적지의 메일 서버를 조사할 경우에는 @ 뒤에 있는 이름이 메일 배송 목적지가 된다.

위 세 가지가 일치하는 정보를 찾아서 보내주는데, 그 결과에는 메일 서버의 이름과 메일 서버의 IP도 포함된다.


2) 도메인 계층

위에서는 하나의 DNS 서버에 이름과 IP 주소가 등록되어 있는 경우를 가정하고 설명했다.

하지만, 실제로 인터넷에는 막대한 수의 서버가 있으므로 이것을 전부 1대의 DNS 서버에 등록하는 것은 불가능하다.

  • 결론부터 말하면 정보를 분산시켜서 다수의 DNS 서버에 등록하고, 다수의 DNS 서버가 연대하여 어디에 정보가 등록되어 있는지를 찾아내는 구조이다.
도메인 명 구조
  • 모든 도메인은 계층적 구조를 가진다.
  • .로 구분된다.
  • www.lab.cyber.co.kr
    • lab, cyber, co, kr 각 각이 하나의 도메인이다.
    • www는 서버의 이름이다.
    • kr은 최상위 도메인이며 왼쪽으로 갈수록 **하위 도메인(서브 도메인)**이다.
  • 이렇게 계층화된 도메인의 정보를 서버에 등록하는데, 이때 하나의 도메인을 일괄적으로 취급한다.
  • 한 대의 DNS서버에 복수의 도메인 정보를 등록할 수 있다.

3) 담당 DNS 서버를 찾아 IP 주소를 가져온다.

다음은 DNS 서버에 등록한 정보를 찾아내는 방법이다.

여기서 중요한 것은 액세스 대상의 웹 서버가 어느 DNS 서버에 등록되어 있는지를 찾아내는 방법이다.

  • kr과 같은 최상위 도메인에서 검색을 시작하는 것 같지만 그 위에 . 이라는 루트 도메인이 존재한다.
  • 예제 - www.lab.glasscom.com. 마지막에 .이 명시되어있다.
담당 DNS 서버를 찾는 과정
  1. 가장 가까운 DNS 서버에 www.lab.glasscom.com에 대한 IP 정보를 요청한다.
  2. 가장 가까운 DNS 서버에는 해당 이름이 등록되어있지 않으므로 기본적으로 등록되어있는 루트 도메인에 해당 정보를 요청한다.
  3. 루트 도메인 은 해당 정보가 없으므로 최상위 도메인(com) 의 DNS 서버 주소를 알려준다.
  4. 최상위 도메인(com) 은 해당 정보가 없으므로 하위 도메인(glasscom) 의 DNS 서버 주소를 알려준다.
  5. 하위 도메인(glasscom) 은 해당 정보가 없으므로 하위 도메인(lab) 의 DNS 서버 주소를 알려준다.
  6. 하위 도메인(lab) 의 DNS 서버는 요청한 IP의 정보를 가장 가까운 DNS 서버에 알려준다.
  7. 가장 가까운 DNS 는 클라이언트에 해당 IP를 알려준다.

4) DNS 서버는 캐시 기능으로 빠르게 회답할 수 있다.

DNS 서버는 한 번 조사한 이름을 캐시에 기록할 수 있는데, 조회한 이름에 해당하는 정보가 캐시에 있으면 그 정보를 회답한다.

캐시
  • 한 번 사용한 데이터를 데이터의 이용 장소와 가까운 곳에 있는 고속 기억 장치에 저장하여 두 번째 이후의 이용을 고속화하는 기술
  • 네트워크에서도 캐시 기술에 의해 고속화하는 방법이 일반화되어 있다.
주의할 점
  • 캐시에 정보를 저장한 후 등록 정보가 변경되는 경우도 있으므로 캐시 안에 저장된 정보는 올바르다고 단언할 수 없다.
  • 따라서 DNS 서버에 등록하는 정보에는 유효 기한을 설정하고, 캐시에 저장한 데이터의 유효 기간이 지나면 캐시에서 삭제해야 한다.
  • 조회에서 회답할 때 정보가 캐시에 저장된 것인지, 아니면 등록처 DNS 서버에서 회답한 것인지 알려준다.



4. 프로토콜 스택에 메시지 송신을 의뢰한다.

1) 데이터 송, 수신 동작의 개요

IP 주소를 조사했으면 IP 주소의 상대, 여기서는 액세스 대상 웹 서버에 메시지를 송신하도록 OS 내부에 있는 프로토콜 스택에 의뢰한다.

디지털 데이터를 송수신하는 동작은 브라우저 뿐만 아니라 네트워크를 이용하는 애플리케이션 전체 공통이다.

"OS 내부의 프로토콜 스택에 메시지 송신 동작을 의뢰할 때는 Socket 라이브러리 프로그램 부품을 결정된 순번대로 호출한다."

데이터 송수신 단계

이 네 가지 동작을 실행하는 것은 OS 내부의 프로토콜 스택이다.

어플리케이션은 Socket 라이브러리의 프로그램 부품을 이용하여 프로토콜 스택에 명령을 전달한다.

  1. 소켓을 만든다(소켓 작성 단계)
  2. 서버측의 소켓에 파이프를 연결한다.(접속 단계)
  3. 데이터를 송수신한다.(송수신 단계)
  4. 파이프를 분리하고 소켓을 말소한다.

2) 소켓의 작성 단계

// 어플리케이션 //

# 1. 준비
<디스크립터> = socket(<IPv4 사용>, <스트림형>, ...); // <IPv4 사용>은 IP의 종류인 IPv4와 IPv6를 명시하기 위해 사용

# 2. 접속
connect(<디스크립터>, <서버의 IP 주소와 포트 번호>, ...);

# 3. 송신
write(<디스크립터>, <송신 데이터>, <송신 데이터 길이>);

# 3'. 수신
<수신 데이터 길이> = read(<디스크립터>, <수신 버퍼>, ...);

# 4. 연결 끊기
close(<디스크립터>);
# 1. 준비
  • Socket 라이브러리의 socket을 이용하여 프로토콜 스택에 소켓 생성을 의뢰한다.
  • 프로토콜 스택은 소켓을 생성하여 디스크립트라는 것을 회신한다.
    • 디스크립터
    • 애플리케이션은 디스크립터라는 번호표 같은 것으로, 소켓을 식별한다.
    • 이것을 이용해서 프로토콜 스택은 어느 소켓을 이용하여 접속할 지 또는 데이터를 송수신할 지 금방 판단한다.

3) 파이프를 연결하는 접속 단계

Socket 라이브러리의 connect 프로그램을 호출하여 이 의뢰 동작을 실행한다.

여기서 핵심은 connect를 지정할 때 지정하는 디스크립터, 서버의 IP 주소, 포트 번호라는 세 가지 값이다.

  • 디스크립터 : 애플리케이션이 소켓을 식별하는 것 (Application과 Protocol Stack 간의 통신에 사용)
  • IP 주소와 포트 번호 : 클라이언트와 서버 간에 상대의 소켓을 식별하는 것(Protocol Stack과의 외부 통신을 위해 사용)
# 2. 접속
connect의 핵심 요소
  • 디스크립터
    • 소켓을 만들 때 받은 값.
    • 여기에서 지정한 디스크립터는 connect가 프로토콜 스택에 통지함
    • 프로토콜 스택은 통지받은 디스크립터를 보고 어느 소켓을 서버측의 소켓에 접속할지를 판단하여 접속 동작을 실행
  • 서버의 IP 주소
    • DNS 서버에 조회하여 조사한 액세스 대상 서버의 IP 주소
    • 데이터 송수신 동작을 실행할 때는 송수신하는 상대의 IP 주소를 프로토콜 스택에 알린다.
  • 포트 번호
    • IP 주소는 네트워크에 존재하는 각 컴퓨터를 식별하기 위해 할당된 서로 다른 값이다.
    • 포트 번호는 컴퓨터의 어느 소켓과 접속할지를 분명히 하기 위해 지정하는 값이다.
    • IP 주소와 포트 번호 두 가지를 지정해야 어느 컴퓨어의 어느 소켓과 접속할지를 분명히 할 수 있다.
    • 디스크립터는 프로토콜 스택이 소켓을 만들도록 의뢰한 애플리케이션에 건내주는 것이지, 접속 상대에게 건내주는 것이 아니다.
    • 서버측의 소켓의 포트는 미리 정의되어있다.
      • 웹은 80번, 메일은 25번
결론
  • connect를 호출하면 프로토콜 스택이 접속 동작을 수행

  • 상대와 연결되면 프로토콜 스택은 연결된 상태의 IP 주소나 포트 번호 등의 정보를 소켓에 기록

  • 데이터 송수신이 가능한 상태가 됨


4) 메시지를 주고받는 송수신 단계

# 3. 송신

송신 시에는 Socket 라이브러리의 write라는 프로그램 부품을 사용하여 프로토콜 스택에 일을 의뢰한다.

  • 애플리케이션은 송신 데이터를 메모리에 준비한다.
    • 사용자가 입력한 URL을 바탕으로 만든 HTTP의 리퀘스트 메시지가 여기서 말하는 송신 데이터이다.
  • write를 호출할 때 디스크립터와 송신 데이터를 지정한다.
  • 프로토콜 스택은 송신 데이터를 서버에게 송신한다.
    • 소켓에는 연결된 상태가 기록되어 있다.
    • 따라서 연결된 상태가 판명되어 그곳을 향해 데이터를 송신한다.
  • 데이터를 받은 서버는 내용을 조사하고 적절한 처리를 실행하여 응답 메시지를 반송한다.
# 3'. 수신

수신 시에는 Socket 라이브러리의 read라는 프로그램 부품을 통해 프로토콜 스택에 일을 의뢰한다.

  • 수신한 응답 메시지를 저장히기 위한 메모리 영역을 지정한다.
    • 이 메모리 영역을 수신 버퍼라고 한다.
  • 그러면, 응답 메시지가 돌아올 때, read가 받아서 수신 버퍼에 저장한다.
  • 수신 버퍼는 애플리케이션 프로그램의 내부에 마련된 메모리 영역이므로 수신 버퍼에 메시지를 저장한 시점에서 메시지를 애플리케이션에 건내준다.

5) 연결 끊기 단계에서 송수신이 종료된다.

Socket 라이브러리의 close라는 프로그램 부품을 호출하여 연결 끊기 단계로 들어가도록 프로토콜 스택에 의뢰한다.

# 4. 연결 끊기
  • 웹에서 사용하는 HTTP 프로토콜에서는 본래 응답 메시지의 송신을 완료했을 때 웹 서버측에서 연결 끊기 동작을 실행한다.
    • 즉, 먼저 웹 서버측에서 close를 호출하여 연결을 끊는다.
  • 그러면 클라이언트측에 전달되어 클라이언트의 소켓은 연결 끊기 단계로 들어간다.
  • 그리고 브라우저가 read로 수신 동작을 의뢰했을 때 read는 수신한 데이터를 건내주는 대신 송수신 동작이 완료되어 연결이 끊겼다는 사실을 브라우저에 통지한다.
  • 이로써 송수신이 종료되었다는 것을 알 수 있으므로 브라우저에서도 close를 호출하여 연결 끊기 단계에 들어간다.
⚠️ **GitHub.com Fallback** ⚠️