[WEEK07] Proxy - Ⅱ

김상호·2022년 5월 18일
0

Development Log

목록 보기
24/45

Proxy

소켓 인터페이스

소켓(socket)

통신을 위한 끝점(End point of a connection). 각 소켓은 인터넷 주소와 16비트 정수 포트로 이루어진 소켓 주소를 가지며, address : port 로 나타낸다. 소켓은 모든 운영체제에서 지원한다. (데이터를 주고 받기 위해서 소켓 디스크립터(socket descriptor)라는 파일시스템을 이용한다.)

Berkeley Socket(BSD Socket)

소켓을 파일 식별자(file descriptor)의 형식으로 소켓을 다루는 API. Unix의 철학인 '모든 것은 파일이다'에서부터 출발했다. 네트워크를 포함한 모든 Unix I/O 디바이스들은 파일이므로, 소켓 역시 네트워크 상의 다른 프로세스와 통신하는 역할을 하는 파일로 볼 수 있다. Internet 소켓들은 대부분 이런 BSD 표준을 따른다.

클라이언트의 소켓 주소 내의 포트는 클라이언트가 연결 요청을 할 때 커널이 자동으로 할당한다. 서버의 소켓 주소에 있는 포트는 대개 영구적으로 이 서비스에 연결되는 Well-known port다. (예시- 웹 서버 포트: 80, 이메일 서버 포트: 25 등)

Internet에서 활용되는 인터넷 소켓 주소는 더 구체적인 정보를 담고 있는 16바이트 구조체에 저장된다. IPv4 소켓에서 사용되는 종류이다. 소켓 주소 구조체는 다음과 같다.

// IP socket address structure
struct sockaddr_in {                // IPv4 주소를 저장하는 구조체
    uint16_t        sin_family;     // 주소 체계를 저장하는 필드(AF_INET)
    uint16_t        sin_port;       // 포트 정보 저장
    struct in_addr  sin_addr;       // IPv4 주소 저장
    unsigned char   sin_zero[8];    // 사용하지 않는 필드. 0으로 채움
};

// Generic socket address structure (for connect, bind, and accept)
struct sockaddr {
    uint16_t       sa_family;       // 주소 체계
    char           sa_data[4];      // 해당 주소 체계에서 사용하는 주소 정보(IP정보 + 포트 정보)
};

sockaddr_in 구조체를 sockaddr 구조체로 캐스팅할 필요가 있을 때는 아래와 같이 사용하면 된다.

// 소켓의 주소를 담는 기본 구조체 틀의 역할(socket.h에 정의되어 있음)
typedef struct sockaddr SA;

소켓 인터페이스 기반 네트워크 응용 프로그램의 개요는 아래의 그림과 같다.

socket( ) 함수

클라이언트와 서버가 소켓(소켓 판별자)를 만들기 위해 사용하는 함수.

int socket(int domain, int type, int protocol)

예시
int clientfd = Socket(AF_INET, SOCK_STREAM, 0);

파라미터

  • 네트워크 주소 체계 domain: AF_INET 32비트 IPv4 주소를 사용한다는 것을 알려준다.
  • 소켓 타입 type: SOCK_STREAM 우리가 만들 소켓의 타입이 TCP 프로토콜을 사용한 통신의 소켓이라는 뜻.

리턴값

  • 파일을 대신할 수 있는 식별자(작은 정수)를 리턴한다.
  • 실패하면 -1

bind( ) 함수

서버의 소켓 주소와 프로세스가 만든 소켓을 서로 묶는다. 커널 콜이다. 즉, 커널이 이 역할을 한다.

  • 응용 프로그램이 socket()을 이용해 소켓을 만들면, 통신에 이 소켓을 사용하기 위해 소켓의 네트워크 시스템(TCP/IP) 주소를 그 소켓에 붙여주어야 한다. 그래야 응용 프로세스와 네트워크 시스템 간의 패킷 전달이 가능하다.
int bind(int sockfd, sockaddr* addr, socklen_t addrlen);

파라미터

  • 서버의 응용 프로그램이 만든 소켓의 번호(식별자) sockfd
  • 서버의 소켓 주소 (sockaddr *) addr 소켓의 다른 한쪽 끝(연결의 끝)의 주소
  • 주소의 길이 addrlen IPv4의 경우는 4이다(바이트).

리턴값

  • 성공했으면 0, 아님 -1.

클라이언트는 왜 bind()가 필요가 없을까?

  • 클라이언트는 포트 번호를 임의로 사용해도 된다. 즉, IP 주소나 포트번호를 다른 클라이언트 또는 서버가 미리 알고 있을 필요가 없다.
  • 서버의 응용 프로그램은 자신이 사용하고 있는 포트 번호를 통하여 클라이언트들의 서비스를 처리해야 하므로, 응용 프로그램이 소켓 번호와 소켓 주소를 bind()하는 것이 필수적이다.

listen( ) 함수

어떤 컴퓨터로부터 요청이 와도 수락할 수 있게 대기상태에 들어가도록 하는 함수, 해당 소켓이 서버 소켓임을 알려준다.

  • socket()이 만든 소켓은 기본적으로 클라이언트 단의 능동적인 소켓이다. 즉, 연결을 요청하는 역할을 한다. 그에 비해 서버 소켓은 클라이언트의 연결 요청을 기다리는 입장이므로, 이렇게 수동적 소켓임을 알려주기 위해 사용한다.
int listen(int sockfd, int backlog);

파라미터

  • 서버의 응용 프로그램이 만든 소켓의 번호(식별자) sockfd
  • 커널이 요청을 거절하기 전에 큐에 저장해야 하는 연결의 수 backlog 일반적으로 1024라는 큰 수를 넣는다.

리턴값

  • 성공했으면 0, 아님 -1.

accpet( ) 함수

서버가 연결 준비가 되었다는 것을 알려주고 연결 request를 받는다.

int accept(int listenfd, SA *addr, int *addrlen);

파라미터

  • 클라이언트의 연결 요청을 받는 듣기 소켓의 번호 listenfd
  • 클라이언트에게서 요청을 받고 나면 알게 되는 클라이언트의 소켓 주소 (sockaddr *) addr
  • 주소의 길이 addrlen

리턴값

  • 연결 식별자 connected descriptor
    • 이를 통해 Unix I/O 함수들을 사용해서 클라이언트와 통신할 수 있다.
  • 실패했으면 -1

connect( ) 함수

서버에 연결 요청하는 함수. 서버가 바쁘면 대기열에 넣었다가 때가 되면 accept

int connect(int clientfd, SA *addr, socklen_t addrlen);

파라미터

  • 클라이언트의 소켓의 번호 clientfd
  • 서버의 소켓 주소 (sockaddr *) addr
  • 주소의 길이 addrlen

리턴값

  • 성공한다면 0, 실패했으면 -1

결과

  • 성공한다면 clientfd 파일을 읽고 쓸 수 있게 된다.
  • 성공한 연결은 (x : y, addr.sin_addr : addr.sin_port)와 같이 소켓 쌍으로 표현할 수 있다.
    • x : 클라이언트 주소
    • y : 클라이언트 호스트 내의 해당 프로세스를 식별하는 유일한 포트 주소

호스트와 서비스 변환

getaddrinfo 함수

getaddrinfo함수는 도메인 주소값을 IP주소값으로 변환할 때 사용하는 함수다. host, service, hint값을 입력하여 result로부터 정보를 추출하는 구조이다.

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
    
int getaddrinfo(const char *host,               // (ex) "www.example.com" or IP
                const char *service,            // (ex) "http" or port number
                const struct addrinfo *hints,   
                struct addrinfo **result);      // DNS서버로부터 받은 네트워크 주소 정보(IP)를 돌려주는 output 매개변수
            //Returns: 0 if OK, nonzero error code on error

// 사용 후 메모리 해제(메모리 누수 방지)
void freeaddrinfo(struct addrinfo *result);
  • host인자에는 도메인 이름이나 정수형인 IP주소를 넣을 수 있다. 호스트 이름을 주소로 변환하고 싶지 않은 경우 이 자리에 NULL을 넣으면 된다.
  • service는 서비스 이름이나 포트 번호를 넣으면 된다. 호스트와 마찬가지로 NULL값으로 설정할 수 있지만, host, service 둘 중 한 개는 명시되어야 한다.
  • hints 인자는 getaddrinfo가 리턴하는 result에 해당하는 구조체의 정보들을 컨트롤한다(선택적으로 사용 가능).
  • result는 DNS서버로부터 받은 네트워크 주소 정보를 돌려주는 output 매개변수. linked-list 자료구조로 이루어진 addrinfo 구조체를 사용한다.

addrinfo 구조체

struct addrinfo {
    int		       ai_flags;       // 기본 동작을 더 수정하는 비트마스크
    int		       ai_family;      // AF_INET -> IPv4, AF_INET6 -> IPv6
    int		       ai_socktype;    // TCP 경우 SOCK_STREAM
    int		       ai_protocol;    
    char  	       *ai_canonname;   
    size_t             ai_addrlen;     // 소켓 주소 구조체의 크기
    struct sockaddr    *ai_addr;        // 소켓 주소 구조체 가리킴
    struct addrinfo    *ai_next;        // 다음 addrinfo구조체 가리킴
};

HTTP, Hypertext Transfer Protocol

웹에서 웹 클라이언트(브라우저)와 웹 서버가 서로 정보를 주고받을 수 있는 응용 계층의 프로토콜 중 하나로, TCP/IP 프로토콜을 이용한다.

HTTP는 이미지, 텍스트, 오디오 등 여러 가지 유형의 데이터들을 주고받을 수 있다. 이런 웹 컨텐츠는 MIME 타입으로 인코딩된다.

MIME 타입, Multipurpose Internet Mail Extensions

웹 서버에서 전송되는 여러 종류의 컨텐츠 유형들을 전달하기 위해 필요한 매커니즘이다.

역할

  • 바이너리 파일의 송수신 웹은 텍스트 파일 뿐만 아니라 여러 바이너리 파일(오디오, 이미지, 비디오 등)을 전송하기도 한다. 이런 바이너리 파일들을 문제 없이 전달하기 위해 파일을 MIME 타입 텍스트파일로 인코딩한다.
  • 데이터의 유형 명시(Content-type) 클라이언트와 서버는 주고받는 데이터 본체의 MIME 타입을 통해 데이터가 어떤 유형인지 파악하고, 어떻게 데이터를 해석할지를 결정한다. 예를 들어, 서버에서 클라이언트에게 준 데이터 본체의 MIME 타입이 text/html이라면, 클라이언트는 이 데이터가 html 형식의 텍스트 데이터라는 것을 알 수 있다. MIME 타입의 예

Content-Type

HTTP 요청, 응답 메세지의 헤더 중에는 MIME 타입으로 표현된 Content-Type 헤더라는 것이 있다. 이 HTTP 메세지의 바디 데이터를 상대가 어떤 MIME 타입으로 해석해야 할 지를 알려준다.

정적 컨텐츠, 동적 컨텐츠

웹에서 컨텐츠는 단지 파일이다. 웹 서버가 클라이언트에게 컨텐츠를 제공하는 유형을 크게 정적, 동적 컨텐츠로 나눌 수 있다.

  • 정적 컨텐츠 : 변화가 없는 컨텐츠. 미리 서버의 파일 시스템에 저장해 둔 내용을 클라이언트에 상관 없이 응답만 한다.
  • 동적 컨텐츠 : 실행파일이 런타임에 만든 출력을 클라이언트에게 보낼 때, 그 출력을 동적 컨텐츠라고 한다. 다시 말해 클라이언트의 요청에 따라 결과값이 달라지는 컨텐츠를 의미한다.

URL, Uniform Resource Locator

웹 네트워크 상에 있는 컨텐츠(자원)가 어디 있는지를 알려주는 주소이다. 작게는 웹 페이지, 크게는 컴퓨터 네트워크 상의 모든 자원의 위치를 나타낼 수 있다.

해당 웹 자원의 주소에 접근하려면 해당 URL에 맞는 프로토콜로 접속해야 한다.

http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2

URL의 prefix

  • 프로토콜 : http 어떤 프로토콜을 사용하여 데이터를 교환할 것인가? ex. HTTP, SSH, FTP 등등...
  • 도메인 이름 : www.example.com 웹 서버가 어디에 있는가?
  • 포트 번호 : :80 이 서버가 어느 포트를 듣고 있는가? 각 프로토콜에 대한 표준 포트를 사용할 시에는 생략 가능. HTTP 프로토콜의 표준 포트는 80이다.

URL의 suffix

  • 웹 서버 안에서 자원의 경로 : /path/to/myfile.html 이 파일은 인터넷 호스트 www.example.com 안의 해당 경로에 있다. 그리고 HTML 유형이다.
  • 웹 서버에 제공하는 추가 파라미터 ?key1=value1&key2=value2
  • 해당 URL이 정적 or 동적 컨텐츠인지 특별한 방법은 없고, 만약 한 디렉토리를 만들어서 모든 동적 컨텐츠가 그 디렉토리에 저장되도록 할 수 있다(예. cgi-bin 디렉토리)

0개의 댓글