06. 웹서버에 도착하여 응답 데이터가 웹 브라우저로 돌아간다. - chwyym/Network GitHub Wiki

1. 서버의 개요

1) 클라이언트와 서버의 차이점

① Socket Library 구조
  - 클라이언트는 접속동작을 수행하고, 서버는 클라이언트의 접속동작을 기다린다.

② 서버의 애플리케이션은 동시에 다수의 클라이언트 PC와 대화한다.

2) 서버 애플리케이션의 구조

  - 클라이언트가 새로 접속할 때마다 새로 서버 프로그램을 작동시켜 1:1로 대화하는 방법을 선택.

① 서버 프로그램 접속을 기다리는 부분과 클라이언트와 대화하는 부분의 둘로 만든다.
② 서버 프로그램을 작동해서 설정 파일 읽기 등의 초기화 동작을 마쳤을 때 접속을 기다리는 부분을 실행한다.
③ 클라이언트가 접속하면 클라이언트와 대화하는 부분을 작동시켜서 그곳에 접속이 끝난 소켓을 건네주어 대화를 진행한다.

※ 서버 OS는 멀티태스크 또는 멀티스레드라는 기능에 의해 다수의 프로그램을 동시에 함께 작동할 수 있다.
※ 이 방법은 클라이언트가 접속했을 때 새로 프로그램을 기동하는 부분에서 다소 시간이 걸리고, 응답 시간이 추가로 소요된다는 단점이 있다. 그래서 미리 클라이언트와 대화하는 몇 개의 부분을 작동시켜두고 클라이언트가 접속했을 때 클라이언트의 상대를 처리하지 않는 비어있는 것을 찾아 여기에 접속한 소켓을 건네주어 클라이언트와의 대화를 계속하는 방법도 있다.

3) 서버측의 소켓과 포트 번호

  - 서버 애플리케이션이 Socket 라이브러리를 호출하는 부분의 개요

(1) Client

① 소켓을 만든다.
② 서버측의 소켓과 파이프로 연결(접속단계)
③ 데이터를 송/수신(송/수신 단계)
④ 파이프를 분리하고 소켓을 말소한다.(연결 끊기 단계)

(2) Server

① 소켓을 만든다.
②-① 소켓을 접속 대기 상태로 만든다(접속 대기 상태 => bind()후 listen() 호출 및 포트번호 지정)
②-② 접속을 접수한다(접속 접수 단계)

③ 데이터를 송/수신(송/수신 단계)
④ 파이프를 분리하고 소켓을 말소한다.(연결 끊기 단계)

(3) 서버측 입장에서 정리

Socket을 호출 -> bind 호출 -> 포트번호 지정 -> accept 호출(아직 클라이언트의 접속 패킷이 도착하지 않은 상태) -> 접속 패킷 도착 -> 응답 패킷 반송 -> 접속 접수 동작 실행 -> 접속 대기의 소켓을 복사하여 새로운 소켓 생성(접속 상대의 정보를 비롯한 제어 정보를 새 소켓에 기록)

※ 99소켓을 지정할 때 소켓에 포트만 지정해주는 경우, 서로 다른 클라이언트에게 동일한 포트를 지정해줄 수 없는 문제가 발생한다. 이를 해결하기 위해 소켓을 지정할 때 서버측의 소켓에 할당한 포트 번호뿐만 아니라 클라이언트측의 포트 번호도 사용하고, IP 주소도 추가하여 다음의 네 가지 정보를 사용한다.
① 클라이언트측의 IP주소
② 클라이언트측의 포트 번호
③ 서버측의 IP 주소
④ 서버측의 포트번호

위의 네 가지 정보는 디스크립터 대신 사용할 수 없다. 왜냐하면 소켓을 만든 직후에 아직 접속하지 않은 상태일 때는 네 가지 정보가 준비되어 있지 않기 때문이다. 또한 소켓을 식별할 때 네 가지 정보를 나타내는 것보다 디스크립터라는 한 개의 정보를 나타내는 쪽이 더 간단하다.

2. 서버의 수신동작

1) LAN 어댑터에서 수신 신호를 디지털 데이터로 변환한다.

(10BASE-T의 경우)

  • LAN 어댑터의 MAC 부분 ① 프리앰블(신호에서 데이터를 구분짓는 클록타이밍을 결정) 추출
    ② 클록을 같은 간격으로 연장
    ③ 클록의 위치에서 신호의 변화의 방향 조사
    ④ 신호의 변화의 방향을 1과 0으로 변형
    ⑤ 패킷의 맨 마지막에 있는 프레임 체크 시퀀스(FCS)라는 오류 검사용 데이터를 이용하여 오류 유무를 검사
    ⑥ 오류가 없다면 맨 앞의 MAC 헤더에 있는 수신처 MAC 주소를 조사하여 패킷지 자신을 수신처로 하여 보낸것인지 판단(이더넷의 기본 동작은 먼저 신호를 LAN 전체로 흘리고 해당자만 신호를 수신한다는 방법을 취하기 때문)
    ⑦ 09디지털 데이터로 되돌린 것을 LAN 어댑터의 내부의 버퍼 메모리에 저장.(여기까지는 LAN 어댑터의 MAC 부분이 실행)

  • LAN 드라이버 부분 ① CPU에 인터럽트를 걸어서 LAN 어댑터에서 CPU로 패킷의 도착을 알림.
    ② CPU는 실행중이던 작업을 중단하고 LAN 드라이버로 실행을 전환
    ③ LAN 드라이버가 실행되면서 LAN 어댑터의 버퍼 메모리에서 수신한 패킷을 추출
    ④ 그리고 MAC 헤더의 타입 필드의 값에 따라 프로토콜을 판별하고, 프로토콜을 처리하는 소프트웨어를 호출

2) IP 담당부분의 수신동작

① 프로토콜 스택에 패킷이 전달되면 우선 IP 담당 부분이 동작하여 IP 헤더를 점검한다.
② IP 헤더의 내용이 규칙에 따라 올바르게 만들어졌는지부터 점검한 후
③ 수신처 IP 주소가 자신을 대상으로 하는지 조사한다.
④-① 패킷이 자신의 것이라면, 다음에는 조각 나누기에 의해 패킷이 분할되었는지 조사한다.
④-② 만일, 분할되어있는 경우에는 패킷을 일시적으로 메모리에 저장해둔다.
④-③ 그리고 분할된 패킷의 조각이 전부 도착한 시점에서 패킷의 조각을 조립하여 원래 패킷으로 복원한다.
⑤ 패킷 복원이 완료되면 IP 헤더의 프로토콜 번호 항목을 조사하여 해당하는 담당 부분(TCP 또는 UDP)에 패킷을 건네준다.

3) TCP 담당 부분이 접속 패킷을 수신했을 때의 동작

① 패킷의 TCP헤더에 있는 SYN이라는 컨트롤 비트가 1로 되어있으면 접속 동작의 패킷이다.
② 이 동작이 실행되면 수신처 포트 번호를 조사하여 이 번호와 같은 번호를 할당한 접속 대기 상태의 소켓이 있는지 확인한다. (없으면 오류통지 반송)
③ 해당하는 접속 대기 소켓이 있으면 패킷을 복사하여 새 소켓을 만들고 여기에 송신처의 IP 주소나 포트번호, 시퀀스 번호의 초기값, 윈도우의 값 등 필요한 정보를 기록한다.
④ 동시에 송신버퍼나 수신 버퍼로 사용하는 메모리 영역을 확보한다.
⑤ 그리고 패킷을 받았음을 나타내는 ACK번호, 서버에서 클라이언트에 보내는 데이터에 관한 시퀀스 번호의 초기값, 클라이언트에서 서버로 보내온 데이터를 받기 위한 수신 버퍼의 빈 용량을 나타내는 윈도우 값 등의 항목을 기록한 TCP헤더를 만들고, 이것을 IP 담당 부분에 의뢰하여 클라이언트에 반송한다.


03. 웹 서버 소프트웨어가 리퀘스트 메시지의 의미를 해석하여 요구에 응한다.

01) 조회의 URI를 실제 파일명으로 변환한다.

  • URI에 서버의 파일 경로와 파일 명으로 파일을 요청할 수 있다.
    • ex) /home/user2/index.html
    • 위와 같이 단순히 URI에 기록되어있는 경로에서 파일을 읽어오면 디스크의 파일에 전부 액세스 할 수 있다는 문제가 있다.
  • 웹 서버에서 제공하는 가상의 디렉토리로 파일을 요청한다.
    • 웹 서버의 내부에서 실제 내부의 디렉토리와 외부에서 보이는 가상의 디렉토리를 대응시킨다.

02) CGI(Common Gateway Interface) 프로그램을 작동하는 경우

  • 웹 서버에서 프로그램을 작동시키는 경우 프로그램에서 처리하는 데이터를 HTTP의 리퀘스트 메시지 안에 넣어 브라우저에서 웹 서버로 보내는 것이 일반적입니다.

  • HTTP 리퀘스트 메시지에 데이터를 포함하는 방식

    • method="GET"

      • URI 뒤에 데이터를 포함한다.

      • GET /cgi/sample.cgi?Field1=ABCDEF&SendButton=SEND HTTP/1.1
        
    • method="POST"

      • HTTP 리퀘스트 메시지의 메시지 본문이라는 부분에 데이터를 내장한다.

      • POST /cgi/sample.cgi HTTP/1.1
        (몇 행의 헤더 필드)
        Field1=ABCDEF&SendButton=SEND
        
  • 웹 서버는 HTTP 리퀘스트의 데이터를 프로그램에 건내준다.

  • 웹 서버는 작동시킨 프로그램으로부터 출력 데이터를 받는다.

  • 내용이 무엇이든지 데이터를 처리한 결과를 클라이언트에 반송한다.

3) 웹 서버로 수행하는 액세스 제어

  • 액세스 제어

    • 조건에 따라 액세스 동작 여부를 설정하는 기능을 말한다.

    • 웹 서버에서 설정하는 조건은 주로 다음의 세 가지이다.

      1. 클라이언트의 주소
      2. 클라이언트의 도메인명
      3. 사용자명과 패스워드
      
  • 클라이언트의 주소

    • accept로 접속을 접수했을 때 클라이언트의 IP 주소를 알 수 있으므로 이것을 점검한다.
  • 클라이언트의 도메인명

    • IP 주소에서 DNS 서버에 도메인 명을 조회한다.
    • 이 때, 도메인명을 위조하여 DNS 서버에 등록하는 공격도 가능하므로
      • 도메인명에서 IP 주소를 조사하여 송신처 IP 주소와 일치하는지 확인한다.
    • 이 방법은 DNS 서버의 조회 메시지가 왕래하는 만큼 시간이 걸린다.
      • 응답 시간이 길어진다.
  • 사용자명과 패스워드

    • 1. 웹 서버가 사용자명과 패스워드를 입력하도록 응답 메시지를 이용해 클라이언트에게 통지
      2. 브라우저는 이 응답 메시지를 받으면 사용자명과 패스워드를 입력하는 화면을 표시
      3. 사용자가 입력한 위 정보를 리퀘스트 메시지에 기록하고 다시 한 번 서버에 액세스
      4. 웹 서버는 통지된 사용자명과 패스워드를 사전에 설정한 것과 대조하여 액세스 가능 여부를 판다. 액세스를 허가하는 경우에는 데이터를 반송
      

4) 응답 메시지를 되돌려 보낸다.

  • 리퀘스트 메시지에 대해 처리가 완료되면 응답 메시지를 반송한다.
  • 웹 서버가 Socket 라이브러리의 write를 호출하여 응답 메시지를 프로토콜 스택에 건내준다.
  • 응답 메시지를 어디에 보내야할지 프로토콜 스택에 알려주기 위해 디스크립터를 함께 통지한다.
  • 송출된 패킷은 스위치나 라우터를 경유하여 클라이언트에게 도착한다.

04. 웹 브라우저가 응답 메시지를 받아 화면에 표시한다.

1) 응답 데이터의 형식을 보고 본질을 판단한다.

  • 응답 데이터의 종류를 판단하는 근거는 몇 가지가 있다.
    • Content-Type
      • Content-Type: text/html
        • 이 헤더에 따르면 데이터는 html 문서이다.
      • /의 왼쪽 부분인 text주 타입 이라고 한다.
        • 데이터의 대분류를 나타낸다.
      • /의 오른쪽 부분인 html서브 타입 이라고 한다.
        • 실제 데이터의 종류를 나타낸다.
      • 위의 예시처럼 데이터의 종류가 text인 경우에는 어떠한 문자 코드를 사용하는지 판단해야 한다.
        • charset = utf-8과 같은 문자 코드의 정보를 부가한다.
        • Content-Type: text/html; charset = utf-8
      • 이 필드를 사용하여 데이터의 종류를 나타내는 방법은 MIME라는 사양에 규정되어있다.
        • Multipurpose Internet Mail Extensions
    • Content-Encoding
      • 데이터의 종류를 조사하고 이 헤더를 조사한다.
      • 압축 기술이나 부호화 기술 같이 어떤 변환을 했는지를 이 헤더에 기록한다.

2) 브라우저 화면에 웹 페이지를 표시하여 액세스를 완료한다.

  • 데이터의 종류에 따라 표시 동작이 달라진다.
  • HTML
    • 태그의 의미를 해석하여 문장을 배치하면서 화면에 표시한다.
    • 실제 화면 표시 동작은 OS가 담당한다.
    • 화상 데이터
      • HTML의 문장 데이터 안에 화상이 내장되었음을 나타내는 태그를 사용한다.
      • 브라우저는 이 태그를 발견하면 화상 데이터의 파일을 서버에서 읽어온다.
    • HTML 문서나 화상 데이터 같이 브라우저가 자체에서 표시 기능을 가지고 있는 것은 OS에 지시를 내려 화면에 표시한다.
    • 워드프로세서나 프레젠테이션 같은 것은 브라우저가 자체 표시 기능을 가지고있지 않으므로
      • 해당 어플리케이션을 호출하여 표시한다.
⚠️ **GitHub.com Fallback** ⚠️