11.1 클라이언트 - 서버 프로그래밍 모델
- 모든 네트워크 응용 프로그램은 클라리언트-서버 모델에 기초하고 있다.
11.4 소켓 인터페이스
- 소켓은 네트워크 상에서 서로 다른 시스템 간에 데이터를 교환할 수 있게 해주는 엔드 포인트이다.
- 주로 TCP / IP 프로토콜을 사용하여 통신한다.
11.4.1 소켓 주소 구조체
- 인터넷 소켓 주소는 위 그림과 같이 sockaddr_in 타입의 16바이트 구조체에 저장된다.
인터넷 응용에 대해 sin_family 필드는 AF_INET이다.
sin_port 필드는 16비트 포트 번호이다.
sin_addr 필드는 32비트 IP 주소이다.
IP 주소와 포트 번호는 항상 네트워크 바이트 순서(big-endian)로 저장된다.
AF_INET이란?
- AF_INET은 소켓이 통신할 수 있는 주소 유형(이 경우 IP_v4 주소)을 지정하는데 사용되는 주소 체계이다.
- 소켓을 만들 때는 해당 주소의 패밀리를 지정해야하며, 해당 유형의 주소만 소켓과 함께 사용할 수 있다.
Big-endian이란?
- 빅 엔디안 방식은 낮은 주소에 데이터의 상위 바이트(MSB, Most Significant Bit)부터 저장하는 방식이다.
11.4.2 socket 함수
- 네트워크 통신을 위한 소켓을 생성하는 함수로, 소켓을 식별하는 소켓 디스크립터(소켓 식별자)를 반환한다.
ex) 만약 소켓을 엔드 포인트로 만들고 싶다면 다음과 같이 하드코딩된 인자로 socket 함수를 호출하면 된다.
AF_INET은 우리가 32비트 IP주소(IP_v4)를 사용하고 있다는 것을 나타내고, SOCK_STREAM은 소켓이 인터넷 연결의 엔드 포인트가 될 것이라는 것을 나타낸다.
getaddrinfo 함수를 통해 socket 함수에 들어갈 인자들을 자동으로 생성하여 코드가 프로토콜에 무관하게 돌아갈 수 있게 할 수 있다.
- socket 함수로 만들어진 식별자는 아직 읽거나 쓸 수 없다. 오픈 과정을 완료하는 것은 클라이언트의 소켓인지, 서버의 소켓인지에 따라 다르다.
11.4.3 connect 함수(클라이언트 측)
- 클라이언트가 서버에 연결을 요청하는 함수
- 인자로 서버의 주소가 필요하다.
- addr은 서버의 소켓 주소로, connect 함수는 해당 주소를 통해 서버와 인터넷 연결을 시도한다.
- addrlen은 sizeof(sockaddr_in)이다.
- connect 함수는 연결이 성공하거나 에러가 발생할 때까지 블록되어 있다.
만약 성공한다면, clientfd 식별자는 이제 읽거나 쓸 준비가 되었다는 의미고, 이 연결은 다음과 같은 소켓 쌍으로 규정된다.
x는 클라이언트의 IP 주소이고, y는 클라이언트 호스트의 클라이언트 프로세스를 유일하게 식별하는 단기 포트이다.
- 이 함수도 getaddrinfo를 통해 인자를 생성하여 넣을 수 있다.
11.4.4 bind 함수(서버 측)
- 서버 측에서 사용하는 함수로, 소켓에 특정 주소(IP 주소와 포트 번호)를 할당한다.
- bind 함수는 커널에게 addr에 있는 서버의 소켓 주소를 소켓 식별자 sockfd와 연결시킨다.
- addrlen은 위와 마찬가지로 sizeof(sockaddr_int)이다.
- getaddrinfo 함수를 통해 인자를 제공할 수 있다.
11.4.5 listen 함수(서버 측)
- 서버가 클라이언트의 연결 요청을 기다리도록 설정한다.(클라이언트가 연결에 대해 능동적이고, 서버는 수동적)
- 이 함수는 sockfd에 해당하는 소켓을 능동 소켓에서 듣기 소켓(클라이언트로부터의 연결 요청을 승락할 수 있는 소켓)으로 변환한다.
- 이 함수는 인자 backlog를 통해 연결 대기 큐의 크기를 지정한다.(커널이 요청들을 거절하기 전에 큐에 저장해야 하는 연결의 수, 대부분 1024와 같은 큰 값으로 설정)
11.4.6 accept 함수(서버 측)
- 클라이언트의 연결 요청을 수락하는 함수
- 이 함수는 새로운 소켓의 식별자를 반환하는데, 이 새로운 소켓은 클라이언트와의 통신에 사용된다.
서버에서 연결을 대기하는 소켓을 듣기 소켓(listen socket), 클라이언트와의 통신에 사용되는 새로운 소켓을 연결 소켓(connect socket)이라 한다.
- 위와 같은 과정으로 클라이언트와 서버의 연결이 이루어진다고 보면 된다.
소켓 닫기 - close 함수
- close() 함수는 사용이 끝난 소켓을 닫는 함수이다.
- 이 함수는 리소스를 해제하고, 더 이상의 데이터 전송을 중단한다.
ex) close(sock)
11.4.7 호스트와 서비스 변환
============== 내용 추가 예정 ===============
11.5 웹 서버
11.5.1 웹 기초
- 웹 클라이언트와 서버는 HTTP(Hypertext Transfer Protocol)라고 하는 텍스트 기반 응용 수준 프로토콜을 사용해서 상호 연동한다.
HTTP는 브라우저라고 하는 웹 클라이언트가 서버로의 인터넷 연결을 오픈하고 컨텐츠를 요청하면 서버가 요청한 컨텐츠로 응답하고 연결을 닫아주고, 브라우저가 컨텐츠를 읽고 스크린에 보여주는 간단한 프로토콜이다.
- HTTP와 FTP(File Transfer Protocol)의 주요 차이점으로는 웹 컨텐츠가 HTML(Hypertext Markup Language)이라는 언어로 작성될 수 있다는 것이다.
HTML 페이지는 태그를 포함하고 있어 브라우저에게 여러 텍스트와 그래픽 객체를 페이지에 어떻게 표시할지를 알려준다.
ex) 다음과 같은 예제는 <b>, </b> 태그 사이의 텍스트를 굵은 타입으로 표시하라고 알려준다.
다음과 같이 텍스트 객체 "Carnegie Mellon"에 밑줄을 긋고, CMU 웹 서버에 저장된 index.html이라는 HTML 파일로의 하이퍼링크를 생성하라고 알려줄 수 있다.
사용자가 위 객체를 클릭하면 브루아저는 해당 HTML 파일을 CMU 서버에 요청하고 화면에 표시한다.
11.5.2 웹 컨텐츠
- 웹 클라이언트와 서버에게 컨텐츠는 그 컨텐츠와 관련된 MIME(Multipurpose Internet Mail Extensions) 타입을 갖는 바이트 배열이다.
- 웹 서버는 두 가지 방법으로 클라이언트에게 컨텐츠를 제공한다.
· 디스크 파일을 가져와 그 내용을 클라이언트에게 보내는 방식. 디스크 파일은 정적 컨텐츠(static content)라 하며, 파일을 클라이언트에게 돌려주는 작업은 정적 컨텐츠를 처리한다고 한다.
· 실행파일을 돌리고 그 결과값을 클라이언트에게 보내는 방식. 실행파일이 런타임에 만든 output을 동적 컨텐츠(dynamic content)라 하며, 프로그램을 실행하고 그 결과를 클라이언트에게 보내주는 과정을 동적 컨텐츠를 처리한다고 한다.
- 웹 서버가 리턴하는 모든 내용들은 서버가 관리하는 파일에 연관된다.
이 파일 각각은 URL(Universal Resource Locator)이라고 하는 고유의 이름을 가진다.
ex) 다음과 같은 URL은
포트 80에서 듣고(listen) 있는 웹 서버가 관리하는 인터넷 호스트 www.google.com 의 /index.html이라는 는 HTML 파일을 지정한다.
포트 번호는 옵션이며, HTTP 포트 80이 기본값이다.
실행 파일을 위한 URL은 파일 이름 뒤에 프로그램 인자를 포함할 수 있다.
'?' 문자는 파일 이름과 인자를 구분하며, 각 인자는 '&'로 구분된다.
ex) 다음 URL은
/cgi-bin/adder라는 실행파일을 식별하고, 이 파일은 2개의 인자와 함께 호출된다.(15000과 213)
- 클라이언트와 서버는 트랜잭션(transaction) 동안에 URL의 서로 다른 부분을 사용한다.
위의 예제를 사용하면 클라이언트는 http://www.google.com:80 이라는 접두어를 사용하여 어떤 종류의 서버에 접속해야 하는지 결정하고, 어디에 서버가 있는지, 서버가 무슨 포트를 듣고 있는지를 결정한다.
서버는 /index.html 이라는 접미어를 사용하여 자신의 파일 시스템 상의 파일을 검색하고, 이 요청이 정적 또는 동적 컨텐츠에 대한 것인지 결정한다.
- 서버가 URL의 접미어를 어떻게 해석하는지에 대해 알아야될 사실이 있다.
· URL이 정적 또는 동적 컨텐츠를 참조하는지를 결정하기 위한 표준 규칙은 없다. 각 서버는 자신만의 규칙을 갖고 파일들을 관리한다.
ex) cgi-bin 같은 디렉토리 집합을 지정하여 이 디렉토리에 모든 실행파일들이 들어있게 하는 방법이 있다.
· 접미어 앞 부분의 '/' 는 리눅스의 루트 디렉토리를 나타내는 것은 아니다. 오히려 어떤 종류의 컨텐츠가 요청되든 홈 디렉토리를 나타낸다.
ex) 서버는 /usr/httpd/html 디렉토리에 모든 정적 컨텐츠가 저장되고, /usr/httpd/cgi-bin 디렉토리에 모든 동적 컨텐츠가 저장되도록 구성할 수 있다.
· 최소한의 URL은 '/' 문자이며, 모든 서버는 이 문자를 통해 /index.html과 같은 특정 페이지로 확장한다. 이 특성은 어떤 사이트의 홈페이지를 단순히 브라우저에 도메인 이름만 입력하고도 가져올 수 있는지를 알려준다.
브라우저는 URL에서 빠진 '/'를 추가하고 이 것을 서버에 전달하며, 서버는 '/'를 어떤 기본 파일 이름으로 확장한다.
11.5.3 HTTP 트랜잭션
- HTTP가 인터넷 연결 위로 전송된 텍스트 라인들에 기초하고 있기 때문에 리눅스 TELNET 프로그램을 사용해서 인터넷 상의 모든 웹 서버와 트랜잭션을 실행할 수 있다.
HTTP 요청(Requests)
- HTTP request는 요청 라인(예시의 5번 줄)과 그 이후의 0개 이상의 요청 헤더(6번 줄), 헤더 리스트들을 종료하는 빈 텍스트 줄(7번 줄)이 따라온다.
- 요청 라인은 다음과 같은 형태를 갖는다.
HTTP는 많은 메소드(method)를 지원하며, 여기에는 GET, POST, OPTIONS, HEAD, PUT, DELETE, TRACE 등이 포함되어 있다.
- 대표적인 9가지 메소드
· GET : 리소스를 조회
· POST : 데이터 추가, 등록
· PUT : 리소스 대체, 수정 / 해당 리소스가 없으면 새롭게 생성
· DELETE : 리소스 삭제
· PATCH : 리소스 부분 변경(수정)
· HEAD : GET과 동일하나, HTTP 메세지의 body 부분을 제외하고 조회
· OPTIONS : 서버와 브라우저가 통신하기 위한 통신 옵션을 확인하기 위함
서버가 어떤 method, header, content-type을 제공하는지 알 수 있다.
· CONNECT : 대상 자원으로 식별되는 서버에 대한 연결 요청
· TRACE : 요청 리소스가 수신되는 경로를 보여줌
자기 앞으로 요청을 루프백(반환)시켜준다.
(자세하게 정리한 글: 추가할 예정)
- GET 메소드는 서버에게 URI(Uniform Resource Identifier)에 의해 식별되는 내용을 리턴할 것을 지시한다.
URI는 파일 이름과 옵션 인자들을 포함하는 URL의 접미어다.
- 요청 라인의 version 필드는 요청이 준수하는 HTTP 버전을 나타낸다.
- 요청 헤더는 서버에 브라우저의 이름이나 브라우저가 이해하는 MIME 타입 같은 추가 정보를 제공한다. 요청 헤더는 다음과 같은 형태를 갖는다.
- 그림 11.24의 예제에서의 헤더는 HTTP/1.0에서 요청된 Host 헤더(6번 줄)이다.
Host 헤더는 프록시 캐시(proxy cache)에서 사용되고, 이 캐시는 브라우저와 요청된 파일을 관리하는 본래의 서버 사이의 중간자 역할을 한다.
클라이언트와 서버 사이에는 프록시 체인 내에서 다중 프록시가 존재할 수 있다.
원점 서버의 도메인 이름을 식별하는 Host 헤더의 데이터는 프록시 체인의 중간에 있는 프록시가 캐시된 요청한 컨텐츠의 사본을 가질 수 있는지 결정할 수 있도록 한다.
- 그림 11.24의 7번 줄의 빈 텍스트 줄은 헤더를 종료하고, 서버에게 요청한 HTML 파일을 보낼 것을 명령한다.
HTTP 응답(Responses)
- HTTP 응답은 응답 라인(8번 줄), 요청 헤더들(9 ~ 13번 줄), 헤더를 종료하는 빈 줄(14번 줄), 응답 body의 순으로 온다.
- 응답 라인은 다음과 같은 형태를 가진다.
version 필드는 response가 준수해야 할 HTTP 버전을 설명한다.
status-code(상태 코드)는 3비트 양수로, 요청의 특성을 나타낸다.
status-message(상태 메세지)는 에러 코드를 영어로 나타낸 것이다.
다음 그림은 보편적인 상태 코드와 해당 메세지를 나타낸다.
- 그림 11.24의 9 ~ 13번 줄의 응답 헤더는 응답에 대한 추가적인 정보를 제공한다.
그 중 가장 중요한 헤더는 클라이언트에게 응답 body 내의 컨텐츠의 MIME 타입을 알려주는 Content-type(12번 줄)과 그 크기를 바이트로 나타낸 Content-Length(13번 줄)이다.
11.5.4 동적 컨텐츠의 처리
- 어떻게 클라이언트가 프로그램의 인자들을 서버에 전달하는지, 어떻게 서버가 그 인자들을 자신이 만든 자식 프로세스들에게 넘겨주는지 등을 CGI(Common Gateway Interface)로 설명가능 하다.
어떻게 클라이언트는 프로그램 인자들을 서버에 전달하는가?
- GET 요청을 위한 인자들은 URI(파일 이름과 옵션 인자들을 포함하는 URL의 접미사)에서 전달된다.
- '?' 문자는 파일 이름과 인자를 구분하며, 각 인자는 '&' 문자로 구분한다.
- 빈칸은 인자들 사이에 허용되지 않고, "%20" 스트링으로 표시해야 한다.
어떻게 서버는 인자들을 자식으로 전달하는가?
- 서버가 다음과 같은 요청을 받았다고 하자.
그러면 fork를 호출해서 자식 프로세스를 생성하고, execve를 호출해서 /cgi-bin/adder 프로그램을 자식의 컨텍스트에서 실행한다.
adder 같은 프로그램들은 종종 CGI 프로그램이라고 부르는데, 그 이유는 이들이 CGI 표준의 규칙을 준수하기 때문이다.
execve를 호출하기 전에 자식 프로세스는 CGI 환경변수 QUERY_SRING을 "15000&213"으로 설정하고, adder 프로그램은 런타임에 리눅스 getenv 함수를 사용해서 이 값을 참조할 수 있다.
어떻게 서버가 자식에게 다른 정보를 전달하는가?
- CGI는 CGI 프로그램이 실행될 때 설정되어 있어야 하는 다른 환경변수들의 개수를 정의한다. 다음 그림은 이 중 일부를 보여준다.
자식은 자신의 출력을 어디로 보내는가?
- CGI 프로그램은 자신의 동적 콘텐츠를 표준 출력으로 보낸다.
자식 프로세스가 CGI 프로그램을 로드하고 실행하기 전에 리눅스 dup2함수를 사용해서 표준 출력을 클라이언트와 연계된 연결 식별자로 쟂지정한다.
이로 인해 CGI 프로그램이 표준 츨력으로 쓰는 모든 것은 클라이언트로 직접 가게 된다.
- 부모가 자식이 생성한 컨텐츠의 종류와 크기를 알지 못하기 때문에 자식이 Content-type과 Content-length 응답 헤더와 헤더를 종료하는 빈 줄까지 생성해야한다.
- 그림 11.27은 두 인자를 더하고 클라이언트에게 겨로가와 HTML 파일을 리턴하는 간단한 CGI 프로그램을 보여주고,
그림 11.28은 adder 프로그램으로부터 동적 컨텐츠를 처리하는 HTTP 트랜잭션을 보여준다.
'TIL & WIL' 카테고리의 다른 글
[WIL] 크래프톤 정글 8~9주차 - PintOS 키워드 정리 (0) | 2024.05.21 |
---|---|
[TIL] 크래프톤 정글 7주차 - 프록시(Proxy) (0) | 2024.05.07 |
[TIL] 크래프톤 정글 7주차 - OSI 7 layer(TCP / IP layer) (0) | 2024.05.06 |
[TIL] 크래프톤 정글 7주차 - Socket이란? (1) | 2024.05.06 |
[WIL] 크래프톤 정글 6주차 - Malloc Lab (0) | 2024.05.03 |