CS/Network

[Network] 네트워크 원리(6) 웹 서버에 도착하여 응답 데이터가 웹 브라우저로 돌아간다

sebinChu 2024. 11. 5. 21:40

개요


성공과 실패를 결정하는 1% 네트워크 원리를 공부하고 정리한 글이다.

 

 

STORY 1 | 서버의 개요


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

서버 머신은 용도에 따라 다양한 OS, 하드웨어가 있다. 하지만 네트워크에 관한 부분( LAN 어댑터, 프로토콜 스택, 소켓 라이브러리 등)의 기능은 클라이언트와 서버가 같은 구졸르 갖는다.

→ TCP/IP 기능은 하드웨어나 OS가 무엇이든지 달라지지 않기 때문에 기능이 통일되어 있다고 봐도 무방하다.

 

■ 그렇다면 차이점은?

  • 소켓 라이브러리의 connection과 데이터 송수신
  • 서버 애플리케이션은 동시에 다수의 클라이언트 PC와 대화
    • 하나의 프로그램으로 여러 클라이언트를 다루는 것은 X. 보통은 1:1로 대화

 

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

  • (b): 클라이언트가 새로 접속할 때마다 잇달아 기동되며, 서버와 클라이언트가 일대일로 대응한다.
  • 서버는 소켓을 미리 만들어 두고 접속 대기 상태로 변경한다.

 

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

■ 서버의 연결과정

  1. [디스크립터] 소켓 작성
  2. [bind] 소켓에 포트 할당
  3. [listen] 대기 상태라는 정보를 기록
  4. [accept] 소켓 복제 후, 접속을 접수하는 대기

■ 접수를 기다리는 첫 소켓은 계속 복제용으로 사용된다.

접속 대기 중인 상태에서 accept를 호출하고, 클라이언트의 요청을 받을 때 클라이언트의 소켓과 접속하기 위한 소켓은 접속 대기 중인 소켓을 복제해서 만든다.

  • 새소켓을 만들지 않고 접속 대기 소켓을 그대로 사용하면, 다음 클라이언트가 접속할 때 곤란해진다.
  • 이러한 사태를 막기 위해 소켓은 항상 복제해서 사용된다.

■ 접속 대기 중인 소켓을 복제해서 쓰면 포트 번호는 어떻게 구분할까?

포트 번호는 소켓을 식별하기 위해 사용되는 것이다. 하지만 소켓들이 모두 다른 포트 번호를 사용하는 것은 아니다. 소켓은 총 4 가지 정보를 통해 식별된다.

  • scr IP
  • scr Port
  • des IP
  • des Port

*서버 측의 포켓에는 같은 포트 번호를 가진 여러 개의 소켓이 존재한다. 하지만 클라이언트 측의 소켓은 모두 다른 포트 번호를 할당하므로, 클라이언트 측의 포트 번호에 따라 소켓을 지정할 수 있다.

 

STORY 2 | 서버의 수신 동작


2-1) ⭐️⭐️LAN에 데이터가 도착했을 때

  1. (도착한 패킷의) 프리앰블 부분에서 클록을 추출한다.
  2. 추출한 클록을 같은 간격으로 연장한다.
    • 프리앰블은 신호가 일정 간격으로 규칙적으로 변화한다. 변화의 타이밍을 조사하면 클록이 어느 위치에 있는지 알 수 있다.
  3. 클록이 위치한 곳을 찾고, 여기서 데이터 신호의 변화 방향(+,- 전압)을 조사한다.
  4. 이 변화방향을 통해서 디지털 신호(1,0)로 바꾼다.

*클록: 타이밍 신호를 제공하는 것. 딱 이 시점에 데이터를 전송하면, 온전한 데이터 전송이 가능하다. 

 

 

■ 전기 신호 → 디지털 데이터를 추출하고…

  1. 패킷의 맨 마지막에 있는 프레임 체크 시퀀스(FCS)를 통해 오류 유무를 검사한다.
  2. 패킷 맨 앞의 MAC 헤더의 수신처를 조사해서 자신이 이 패킷의 수신처가 맞는지 확인한다.
    • 이더넷의 원리에 따르면 일단 패킷 뿌리고 받는 쪽이 확인하는 구조
  3. 이 디지털 데이터를 LAN 어댑터 내부의 버퍼 메모리에 저장한다.
  4. CPU는 다른 일을 하고 있기에 인터럽트로 패킷이 도착했음을 알린다.

■ 인터럽트가 실행된 CPU는

  1. 실행하고 있던 작업을 중단하고 LAN 드라이버로 실행을 전환한다.
  2. LAN 드라이버가 버퍼에 있는 패킷을 추출하고, MAC 타입에 따른 프로토콜을 판별한다.
  3. 프토로콜을 처리하는 장치를 호출한다.
  4. IP 프로토콜이라면 TCP/IP의 프로토콜 스택을 호출하고, 패킷을 건네준다.

 

 

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

  1. 대상이 자신인지 주소를 확인한다.
  2. Fragmentation에 대해 확인한다. 만약, 분할되어 있는 경우에는 임시로 메모리에 저장해두고 분할된 패킷이 모두 도작한 시점에 조립한다.
  3. IP 헤더의 프로토콜 항목(TCP or UDP)을 조사후, 담당 부분에 패킷을 건네준다.

 

2-3) TCP 담당 부분이 접속 패킷을 수신했을 때 (3way-handshaking)

■ 서버 측에서 대기 중인 소켓을 확인한다.

TCP 헤더의 SYN = 1이면, 접속 접수 동작을 뜻한다. 따라서 해당 패킷과 같은 포트 번호를 가진 대기 상태의 소켓이 있는지 확인한다. 만약 없으면 그 포트 번호에 해당하는 대기중인 서비스나 프로세스가 없다는 뜻으로, 오류를 통지한다.

  • 예를 들어서 서버 포트는 80인데 클라이언트가 81로 요청해서, 서버의 81에는 대기 중인 소켓이 없는 상황

■ 대기 중인 포트를 가진 소켓을 확인했다면 패킷을 복사하여 소켓을 만들고 TCP 헤더를 만든다.

  1. 대기 중인 소켓의 복제본을 만들고, 여기에 필요한 정보를 기록한다. 동시에 메모리 영역을 확보한다.
  2. 패킷을 받았음을 나타내는 ACK, 시퀀스 번호, 윈도우 값 등을 기록한 TCP 헤더를 만든다.
  3. 만들어진 TCP 헤더를 IP 담당 부분에 보내고, 클라이언트에 반송한다.
  4. 클라이언트는 다시 ACK를 보내고, 접속 동작이 완료된다.

 

 

2-4) TCP 담당 부분이 데이터 패킷을 수신했을 때(전송 과정)

■ TCP 담당 부분이 수신하는 과정

  1. 4 가지 정보를 통해 제대로 도착한 건지 확인한다.
  2. 패킷을 통해 데이터 송수신 진행 상황과 TCP 헤더 정보를 확인하고, 데이터 송수신이 올바르게 동작하고 있는지 점검한다.
  3. 분할된 패킷이라면 수신 버퍼에 저장한다. 지난 번 패킷에서 수신한 데이터 조각의 다음에 연결하는 식으로 데이터가 분할되기 전의 상태로 되돌린다.
  4. 이 과정을 통해 수신 버퍼에 데이터가 저장되면 응답용 TCP 헤더를 만든다.
  5. ACK를 기록하고, IP 담당 부분에 의뢰하여 클라이언트에게 반송한다.

 

■ 애플리케이션이 소켓을 받는 과정

  1. 애플리케이션이 소켓 라이브러리 호출을 통해 데이터를 기다리는 상태를 유지한다.
  2. TCP 담당 부분이 수신 동작이 끝나는 것과 동시에 애플리케이션에게 데이터를 건네준다.
  3. 제어는 애플리케이션으로 넘어가고, HTTP request 내용을 확인한다.
  4. 브라우저에 데이터를 반송한다.

 

2-5) 연결 끊기(4 way handshaking)

■ 서버측에서 연결 끊기를 요청하는 상황이라면

  1. 애플리케이션이 소켓 라이브러리의 close()를 호출한다.
  2. TCP 담당 부분이 FIN = 1을 설정한 TCP 헤더를 만들고, IP 담당 부분에 전송한다.
  3. IP 담당 부분은 클라이언트에 이를 보낸다.
  4. 클라이언트는 ACK를 반송하고, close()를 계속 호출하며 FIN을 보낸다.
  5. 서버가 ACK를 반송하고, 연결이 끊긴다.

 

STORY 3 | 웹서버 SW가 리퀘스트 메시지의 의미를 해석하고 이에 응한다.


3-1) 조회의 URI를 실제 파일명으로 반환한다.

■ 웹 서버의 경우, read에서 받은 데이터의 내용이 HTTP Request Message다.

Request Message에 따라 적절한 처리를 실행하여 응답 메시지를 만든다.

 

■ URI에 기록된 경로는 가상 디렉토리 구조에서의 경로명이다.

Like… ubuntu 심볼릭 링크처럼,

파일을 읽어올 때 가상 디렉토리와 실제 디렉토리의 대응 관계를 조사하고 실제 디렉토리의 경로명으로 변환한 후 파일을 읽어 데이터를 반송한다.

 

3-2) CGI 프로그램을 작동하는 경우

■ 웹 서버에서 프로그램을 작동시키는 경우

단순히 HTTP의 리퀘스트 메시지가 HTML 문서에 액세스할 때와는 다르다. 웹 서버에서 프로그램을 작동시키는 경우, 프로그램에서 처리하는 데이터를 HTTP 리퀘스트 메시지 안에 넣어 브라우저에서 웹 서버로 보내는 것이 일반적이다.

 

■ 리퀘스트 메시지를 보낸 웹 서버의 동작

  1. URI에 쓰여있는 파일명을 통해 이게 프로그램인지 판단한다.
  2. 예를 들어 .php, .exe와 같은 확장자를 등록해두고, 파일명의 확장자가 이와 일치하면 프로그램으로 간주한다.
  3. 프로그램이라고 판단되면 웹 서버는 이 프로그램을 작동시키도록 OS에 의뢰한다.
  4. 리퀘스트 메시지에서 받은 데이터를 작동시킨 프로그램에 전송한다.
  5. 프로그램은 받은 데이터를 처리하여 출력을 웹 서버에 돌려준다. ← 웹 서버는 이에 관여 X

 

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

■ 특정 부서, 특정 사용자 등 조건에 따라 접근을 제어하는 액세스 제어

주로 다음 세 가지에 적용한다.

  1. 클라이언트의 주소
  2. 클라이언트의 도메인명
    • DNS 서버를 이용해서 IP 주소 조사
  3. 사용자명과 패스워드
    • 서버에 사전 등록
    • 클라이언트에게 입력하라는 Request 통지
    • 클라이언트가 입력한 내용과 사전 등록된 암호를 비교 대조

 

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

■ 클라이언트의 요청에 대한 응답을 되돌려 보낸다.

이때 동작 방식은 클라이언트가 Request한 것과 같다.

  • 소켓 라이브러리의 wirte를 호출하여 응답 메시지를 프로토콜 스택에 건네주고, 디스크립터를 통해 통신에 사용되고 있는 소켓 통지

 

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


4-1) 응답 데이터의 형식을 보고 본질을 파악한다.

웹 서버가 전송한 응답 메시지는 다수의 패킷으로 나뉘어 클라이언트에게 도착한다. 클라이언트는 이를 받아, LAN에서 디지털 신호로 변환하고, 프로토콜 스택이 분할된 패킷을 모아서 원래의 응답 메시지로 되돌린 후 브라우저에 건네준다.

 

■ 브라우저가 화면 표시를 할 때는 데이터 유형에 따라 달라진다.

웹에서 취급하는 데이터는 문장, 화상, 음성, 영상 등 다양하고 종류에 따라 표시 방법이 다르다.

 

■ ‘Content-Type’ 헤더를 통해 데이터 종류를 미리 파악한다.

응답 메시지의 맨 앞의 Content-Type 헤더로 종류를 파악하는 것이 원칙이다. 여기에는 다음과 같은 형식의 데이터 종류를 쓴다.

Content-Type: text/html (주타입/서브 타입)

 

이 예시와 같이 데이터의 종류가 텍스트인 경우에는 어떤 문자 코드를 사용하는지 판단해야 한다. 따라서 다음과 같이 charset으로 문자 코드의 정보를 부가한다.

Content-Type: text/html; charset = utf-8

 

*Content-Type에 정의되는 데이터 종류는 MIME 사양에 규정되어 있다. 파일 확장자나 데이터 내용의 포맷 등에서 종합적으로 판단하는 방법도 있다. 예를 들어 .html 이라면 HTML 문서로 간주하거나 문서의 젤 앞부분을 보고 <html>이라고 되어 있으면 HTML 이라고 간주한다.

 

■ ‘Content-Encoding’ 헤더를 통해 데이터 변환에 대해 조사한다.

압축 기술이나 부호화 기술에 따라 원래 데이터를 변환하고 나서 메시지에 저장한 경우에는 어떤 변환을 했는지 이 Content-Encoding 헤더에 기록하고, 응답을 받은 브라우저는 이 필드를 조사하여 필요에 따라 데이터를 원래대로 되돌린다.

 

 

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

데이터 종류가 판명되면 종류에 따라 화면 표시 프로그램을 호출하여 데이터를 표시한다.

 

■ HTML

[기본 html을 화면에 표시]

html의 경우 문장의 레이아웃이나 글꼴의 종류 등을 기록한 태그가 내장되어 있으므로 태그의 의미를 해석하여 문장을 배치하면서 화면에 표시한다. 실제 화면 표시 동작은 OS가 담당하므로, OS에 대해 화면의 어떤 위치에 , 어떤 문자를 어떤 글꼴로 표시할 것인지 지시하는 것이다.

 

[화상 데이터가 포함된 경우]

HTML 문서 데이터와 화상 데이터를 별도의 파일에 저장하고, html의 문장 데이터 안에 화상이 내장되었음을 나타내는 태그를 사용해야 한다.

 

[애플리케이션 데이터의 경우]

플러그인이든 독립된 애플리케이션이든 호출 프로그램이 브라우저에 설정되어 있으므로, 그에 따라 프로그램을 호출하여 데이터를 브라우저에 건네고, 이를 화면에 표시한다.

 

 

드디어 네트워크 스터디가 끝났다...!! 🤗 중요한 내용이니까 앞으로 복기 잘 하자!