지원 서재
작성일
2023. 11. 3. 15:11
작성자
달빛오리

아는 게 좀 나오는 것 같은데, 기대해도 되나요

 

WEEK 07 키워드

BSD 소켓, IP, TCP, HTTP, File Descriptor, DNS

 

📚 키워드 간단 정리
소켓

  • 네트워크 상에서 구동중인 두 프로그램 간에 통신을 할 수 있도록 도와주는 창구 역할
  • BSD 소켓 - C로 된 API로, 네트워크 프로그램을 개발하기 위한 함수의 모음을 제공
  • 소켓이라고 하면 대부분 BSD 소켓을 사용함, BSD 소켓이 성공한 이유는 C로 만들어졌기 때문

 

TCP

  • 전송 제어 프로토콜 (Transmission Control Protocol)
  • 서버와 클라이언트 간에 데이터를 신뢰성 있게 전달하기 위해 만들어진 프로토콜
  • 데이터를 전송하기 전에 전송을 위한 연결을 만드는 연결 지향 프로토콜
  • 네트워크를 통해서 전달될 때 데이터 일부가 손실되거나 순서가 뒤바뀌어서 전달될 수 있는데, TCP는 이를 검색하고 교정해서 순서를 재조합함

 

IP

  • 인터넷 프로토콜 ((Internet Protocol)
  • 데이터 패킷을 네트워크를 통해서 대상에 전달할 수 있도록 데이터 패킷을 라우팅하고 주소를 지정하기 위한 프로토콜
  • 패킷 전달 여부를 보증하지 않고, 패킷을 보낸 순서와 받는 순서가 다를 수 있음
  • 그래서 TCP와 IP를 합친 TCP/IP 프로토콜을 사용함

 

HTTP

  • 하이퍼텍스트 전송 프로토콜, HTML과 같은 하이퍼 미디어 문서를 전송하기 위함
  • 레이어를 나누는 이유
  • 환경이 변할 수도 있기 때문에
  • 다양한 이종 장치들이 나올 수도 있기 때문에

 

File Descriptor (FD)

  • 리눅스/유닉스에서 프로세스가 파일(리소스)를 다룰 때 사용하는 개념
  • 추상적인 개념, 0이 아닌 정수값을 가짐
  • 프로세스의 PID를 검색하여 해당 PID로 파일 디스크립터의 정보를 확인


DNS

  • 도메인(http://www.amazon.com)을 IP 주소(192.0.2.44)로 변환시켜줌
  • 이는 네트워크 내부에서 이루어짐
  • 인터넷 전화번호부라 생각하면 쉬움
  • 도메인은 오로지 사람을 위한 것이여서, 다시 IP 주소로 변환해야 PC가 이해할 수 있음

 

📝 과제 문제

11.6
A. Tiny를 수정해서 모든 요청 라인와 요청 헤더를 echo하도록 하라.

 

B. 여러분이 사용하는 브라우저를 사용해서 Tiny에 정적 컨텐츠를 요청해 보라. Tiny로부터의 출력을 파일로 캡처하라.

 

C. Tiny의 출력을 조사해서 여러분이 사용하는 브라우저의 HTTP 버전을 결정하라.

HTTP/1.1

 

D. RFC 2616의 HTTP/1.1 표준을 참고해서 여러분의 사용하는 브라우저에서 보낸 HTTP 요청 각각의 헤더가 갖는 의미를 결정하라. RFC 2616은 http://www.rfc-editor.org/rfc.html에서 볼 수 있다.

GET / HTTP/1.1
- GET 메서드를 통해, 주소는 '/'로 호출하였다.
- 브라우저의 HTTP 버전은 1.1이다.


Connection: keep-alive
- 연결을 계속 유지하라는 의미, HTTP 1.1부터는 기본적으로 keep-alive로 동작한다.


___internal-request-id: c79dd16f-f146-4627-9c5f-5af98c37e74c
- 이 값은 Unique한 Random Token이며, 매 Request마다 값이 변경된다.
- 요청 시에 Client가 Server로 보내고 Server는 이 헤더값을 기록한 후 다시 Client로 전달한다.
- Timestamp와 IP 주소에 의존하지 않고 해당 로그 항목을 조회할 수 있다는 장점이 있다.

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
- User-Agent 헤더에는 HTTP Request를 보내는 사용자의 소프트웨어 식별 정보를 담고 있다.
- Mozilla 정보/버전 + OS 정보 + 렌더링 엔진 정보 + 브라우저 정보
- Mozilla/5.0는 접속한 브라우저가 Mozilla 5.0과 호환이 가능하다는 의미이다.
- Windows NT 10.0; Win64; x64는 브라우저가 실행되는 OS의 환경을 표시한 것이다.
- AppleWebKit/537.36 (KHTML, like Gecko)는 "Gecko" 같은 브라우저 레이아웃 엔진인 KHTML을 사용한다는 의미이다. AppleWebKit은 KHTML을 기반으로 한 엔진이다.
- Chrome/111.0.0.0 Safari/537.36는 크롬 111.0.0.0 버전에서 요청을 실행했으며, 사파리의 해당 버전과 비슷하단 의미이다.

Accept: /
- Client가 받을 수 있는 메세지 형태(type)를 Server에 알려준다.
- '/'를 기준으로 왼쪽은 컨텐츠 main type, 오른쪽은 컨텐츠 subtype을 뜻한다.
- */*은 왼쪽, 오른쪽이 모두를 뜻하는 *로 되어 있으니, 어떤 컨텐츠든 확인이 가능하다는 뜻이다.

Accept-Encoding: gzip, deflate이 가능하다는 뜻이다.
- Client가 받을 수 있는 압축 방식을 Server에 알려준다.
- 즉 gzip, deflate 방식의 압축 파일만 지원 가능하다는 뜻이다.


Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
- Client가 받을 수 있는 언어의 종류를 Server에 알려준다.
- ';' 기호로 구분하여 명시한다.
- ko-KR을 가장 선호하며, ko, en-US, en의 순으로 지원 가능하다는 뜻이다. 즉 q는 우선 순위를 나타내는 것이다.

 

11.7
Tiny를 확장해서 MP4 비디오 파일을 처리하도록 하시오. 실제 브라우저를 사용해서 여러분의 결과를 체크하시오.

 

11.9
Tiny를 수정해서 정적 컨텐츠를 처리할 때 요청한 파일을 mmap과 rio_readn 대신에 malloc, rio_readn, rio_writen을 사용해서 연결 식별자에게 복사하도록 하시오.

 

11.10
A. 그림 11.27의 CGI adder 함수에 대한 HTML 형식을 작성하시오. 이 형식은 사용자가 함게 더할 두 개의 숫자로 채우는 두 개의 텍스트 상자를 포함해야 한다. 여러분의 형식은 GET 메서드를 사용해서 컨텐츠를 요청해야 한다.

 

B. 실제 브라우저를 사용해서 Tiny로부터 이 형식을 요청하고, 채운 형식을 Tiny에 보내고, adder가 생성한 동적 컨텐츠를 표시하는 방법으로 여러분의 작업을 체크하라.
  • adder.html 파일 생성 - adder 함수에 대한 HTML 파일, 두 개의 숫자값을 사용자로부터 입력받는 화면을 만들었다.
  • tiny.c 파일 수정 - /cgi-bin/adder?value1&value2 형식으로 넘어오면 기존처럼 처리, /cgi-bin/adder 형식으로 넘어오면 adder.html 페이지를 출력하도록 수정했다.
  • adder.c 파일 수정 - 파라미터 값의 형식에 따라 value1, value2를 다르게 파싱하도록 if문으로 나눠 처리했다.

 

11.11
Tiny를 확장해서 HTTP HEAD 메서드를 지원하도록 하라. Telnet을 웹 클라이언트로 사용해서 작업 결과를 체크하시오.
  • 정적 컨텐츠 HEAD 메서드로 호출한 결과와 동적 컨텐츠 HEAD 메서드로 호출한 결과

 

프로젝트 바로가기


❓ 궁금했던 내용 출처

 

프로세스를 만드는데, 왜 "복제" 라는 방법을 써야 하는가?
이유는, 프로세스를 만들기 위해 해야 할 일이 너무나 많기 때문입니다. 커널 내에는 프로세스와 관련한 수 많은 자료구조가 있는데 그걸 일일이 채워넣는것보다는, 그냥 이미 만들어져있는 다른 프로세스의 자료구조를 복사하는게 낫죠. 프로세스가 존재하기 위한 자료구조들을 직접 일일히 채워넣는것은 부팅시에 실행되는 init 프로세스 하나 뿐입니다. 그 외에 모든 프로세스는 이 init 프로세스를 복제하여 생성됩니다.

"복제"하지 않고 프로세스를 생성할때 발생하는 문제점은?
뭐 프로세스를 만드는 과정이 무척 복잡하고 시간이 오래 걸리는 작업이기 때문에 속도나 효율성 문제가 당연히 발생하겠지만, 그것보다는 제 생각은 조금 다릅니다. 새로 생성된 프로세스의 엔트리 포인트를 어디로 할 것인가, 저는 이것이 문제라고 봅니다. 일반적으로, 프로그램은 실행될때 main() 함수를 엔트리 포인트로 하는데, 그렇다고 새로 생성된 모든 프로세스가 main() 함수부터 시작하는것은 대단히 비합리적이고, 또 그로 인해서 야기될 수 많은 문제가 있습니다(부모-자식간의 IPC 문제가 대표적). 그 대신, 부모 프로세스를 복제하면 부모 프로세스가 fork() 를 호출한 시점을 엔트리 포인트로 하는것이 직관적이고, 잠재된 문제를 최소화할 수 있기 때문에 쉽게 납득할 수 있습니다.

fork() 함수로 프로세스를 생성하면 실제로 복제가 이루어지는가?
그렇지 않습니다. 수 많은 자료구조를 채워넣는 과정이 복잡하기 때문에 속도나 편의성면에서 복제를 하긴 하지만, 메모리를 복제한다는 것은 그 나름대로 시간이 오래 걸리는 작업입니다. 그래서 fork() 를 호출한다고 곧바로 복제가 이루어지는 것은 아니고, 위에 답변 다신 분들 말씀대로 "생성된 새 프로세스의 메모리 구간에 뭔가 변화가 일어났을때"에 실제 복사가 수행됩니다.

자식 프로세스가 부모 프로세스의 메모리 공간을 참조하는 포인터를 가지면 왜 안되는가?
우선, 위에 3번에서 설명했듯이, 메모리 복사가 즉시 일어나는 것은 아닙니다. 메모리 복사가 발생할 필요가 나타나기 전까지는, 님이 말씀하신것처럼 자식 프로세스는 부모 프로세스의 메모리 공간을 참조하는 포인터만 가지고 있을 뿐입니다. 복사할 필요성이 생기면, 즉 자식 프로세스의 메모리 공간에 뭔가 write 동작이 일어나면 그때 복사가 이루어집니다. 자식 프로세스의 메모리 공간에 write 동작이 일어날때, 메모리 복사가 이루어지지 않는다면 어떻게 될까요. 자식 프로세스의 메모리는 부모 프로세스의 메모리 공간을 가르키고 있을테고, 자식 프로세스 메모리 공간의 변화가 곧바로 부모 프로세스 메모리 공간의 변화로 반영되어서 결국은 부모-자식 프로세스의 구분이 의미가 없어집니다.

See also
프로세스와 더불어서, 프로그램의 실행 흐름을 분기할 수 있는 또 다른 메서드로 thread라는 것이 있습니다. 이건 그냥 fork() 하는것보다 어렵지만, 더 유용합니다.