[웹서버] 개념 정리

bongf·2022년 5월 16일
0

정글

목록 보기
11/20
post-thumbnail

소켓

  • BSD소켓 : 버클릿 연구자들이 만들어서 Berkeley sockets ( Berkeley Software Distribution) 이라고 불린다.
  • 위키 : 네트워크를 통해서 데이터를 보내고 받는 엔드포인트(endpoint, 끝점)의 역할을 하는 소프트웨어 스트럭쳐다.
    • 소켓의 구조와 속성은 네트워크 아키텍쳐 API(application programming interface)에 의해 정의된다.
  • 프로세스간 통신을 위한, 운영체제가 제공해주는 인터페이스를 소켓이라고 이해했다.
    • 소켓을 통해서 통신할 수 있다
  • 운영체제가 제공하는 시스템 콜들 중에서 네트워크 관련한 것들을 묶어 놓은 것이 소켓 API이다.
  • TCP기반의 소켓을 소켓 스트리밍이라고 하고, UDP기반의 소켓을 소켓 데이터그램이라고 한다

트랜잭션

  • 모든 네트워크 애플리케이션은 클라이언트-서버 모델을 기반으로 한다.
    • 주의. 클라이언트와 서버는 프로세스고, 호스트가 아님을 주의!
      • 한 개의 호스트는 여러 개의 클라이언트와 여러 개의 서버를 동시에 실행할 수 있다
  • 클라이언트-서버 모델에서 기본 연산이 클라이언트-서버 트랜잭션이다
    • 요청과 응답으로 이루어진다
  • 클라이언트와 서버는 소켓 인터페이스를 통해서 연결을 수립한다.

네트워크

  • 컴퓨터와 컴퓨터가 연결되어 있는 것
  • 호스트에게 네트워크는 또 다른 I/O디바이스다.

계층 구조 시스템

  • 스위치는 layer2스위치, layer3스위치가 있다

  • LAN Local Area Network 제한된 구역을(ex.건물 안) 범위로 갖는 네트워크

  • WAN Wide area network 넓은 범위를 커버하며, 임대된 통신 회선도 포함한다.

    • ISP(인터넷 공급자, 임대된 통신 회선)가 LAN-ISP-LAN 을 연결할 경우 WAN
  • 이더넷 LAN에서 데이터를 주고 받으려면 네트워크 장비 간에 신호를 주고 받는 규칙을 정하는 계층인 데이터 링크 계층 기술이 필요한데, 그 중 가장 많이 사용되는 것이 이더넷이다 (출처: 책, 모두의 네트워크)

    • 네트워킹의 방식(통신 방식)이고, CMMA/CD 방법으로 충돌을 회피하는 등의 방식으로 통신한다.
  • 이더넷 세그먼트 : 몇개의 전선과 허브로 구성

  • 허브 랜을 구성할 때 한 사무실, 가까운 거리의 장비들을 '케이블'을 사용하여 연결하는 장치

    • 허브는 broadcast하기 때문에 목적지와 상관 없이 다 전송
  • 이더넷 어댑터 에는 MacAddress가 있다(globally unique, 48비트)

  • 브릿지

    • 요즘은 스위치를 사용
    • 전체 빌딩, 캠퍼스 규모 등 더 큰 범위의 브릿지 이더넷
    • 브릿지, 스위치는 허브가 브로드캐스팅 했던 것과 달리 필요한 목적지에만 전달한다.
    • 스위치는 처리방식이 하드웨어, 브릿지는 소프트웨어 적으로 처리해서 속도는 스위치가 빠르다고 한다.
  • 라우터 네트워크를 분리할 수 있다

    • 목적지 IP 주소까지 어떤 경로로 데이터를 보낼지 결정하는 것을 라우팅(routing)이라고 한다.
    • WAN의 사례 출처 : 책, 텀퓨터 시스템
  • 네트워크 프로토콜 소프트웨어 계층이 제공해야 하는 2가지 기능(네트워크 마다 비호환일 수 있잖아 이들을 통신하게 하기 위해서)

    • 1) 명명법 : 각 호스트는 자신을 유일하게 식별하는 internet 주소 최소 한 개를 가진다
    • 2) internet 프로토콜은 패킷이라는 단위로 묶어서 패키징 하기 때문에 서로 다른 네트워크 기술에서도 이 같은 패킷을 사용하여 호환이 될 수 있다

라우터를 이용한 네트워크 과정

  • 출처 : 컴퓨터 시스템 책
  1. 호스트 A의 클라이언트는 클라이언트의 가상 주소공간에서 커널 버퍼로 데이터를 복
    사하는 시스템 콜을 호출한다.
  2. 호스트 A의 프로토콜 소프트웨어는 internet 헤더와 LAN1 프레임 헤더를 데이터
    에 추가해서 LAN1 프레임을 생성한다. internet 헤더는 internet 호스트 B로 주
    소가 지정된다. LAN1 프레임 헤더는 라우터로 주소가 지정된다. 호스트 A는 이
    프레임을 어댑터로 전달한다. LAN1 프레임의 데이터가 internet 패킷이고, 그 데
    이터는 실제 사용자 데이터라는 점에 유의하라. 이런 종류의 캡슐화 encapsulati0n는
    internetwoking의 근본적 인 통찰들 중의 하나다.
  3. LAN1 어댑터는 이 프레임을 네트워크로 복사한다.
  4. 프레임이 라우터에 도달하면, 라우터의 LAN1 어댑터는 전선에서 이것을 읽어서 프
    로토콜 소프트웨어로 전달한다
  5. 라우터는 internet 패킷 헤더에서 목적지 internet 주소를 가져와서 패킷을 전달할
    곳을 결정하기 위해, 이 경우에는 LAN2 라우팅 테이블에서의 인덱스로 이것을 사용
    한다. 라우터는 이전의 LAN1 프레임 헤더를 벗겨내고, 호스트 B의 주소를 갖는 새
    로운 LAN2 프레임 헤더를 앞에 붙여서 이것을 어댑터로 전달한다.
  6. 라우터의 LAN2 어댑터는 이 프레임을 네트워크로 복사한다.
  7. 이 프레임이 호스트 B에 도착하면 어댑터는 이 프레임을 전선에서 읽어들이고, 이것
    을 프로토콜 소프트웨어로 넘긴다.
  8. 마지막으로, 호스트 B의 프로토콜 소프트웨어는 패킷 헤더와 프레임 헤더를 벗겨낸
    다. 프로토콜 소프트웨어는 최종적으로 이 데이터를 서버가 이 데이터를 읽는 시스템
    콜을 호출할 때 서버의 가상 주소공간으로 복사한다.

IP

  • IP 주소는 비 부호형 32비트 정수 (unsigned 32-bit integer)
  • IP는 Internet Domain Name 라고 불리는 도메인에 매핑된다. (www.naver.com)
  • 빅엔디안 바이트 순서로 통일
    • 그렇기 때문에 호스트가 리틀 엔디안을 쓸 경우에도 IP주소는 빅엔디안으로 저장한다(컴퓨터시스템 책 191)
  • IP : IP전송이 패킷 전송을 보장하지 않아 TCP로 보완하여 보장
    • UDP도 있다.

IP 주소 표기법

  • 호스트마다 빅엔디안, 리틀 엔디안 다르게 쓸 수 있기 때문에
    • unit32_t htonl(); unit32_t htons(); (long, short) : 네트워크방식(빅엔디안)으로 변경
    • unit32_t ntohl(); unit32_t ntohs(); 호스트가 사용하는 방식으로 변경
  • dotted-decimal 스트링 -> IP주소(16진수) : inet_pton() 함수 사용하여 변환
  • IP주소(16진수) -> dotted-decimal 스트링 : inet_ntop() 함수 사용하여 변환
  • 점으로 구분할시 8bit씩 갖기 떄문에 16진수를 2자리씩 끊어주면 된다.

Internet Doamin Name

  • 다수의 도메인이 하나의 IP에 할당 가능, 다수의 IP가 하나의 도메인에 할당 가능, 일대일 가능
  • 도메인은 계층 구조를 가지고 있다.
  • 출처 : 컴퓨터 시스템
  • 루트 네임 서버는 전세계에 분산되어 같은 카피를 갖고 있다(13개)
  • 루트 네임 서버 밑에 TLD (Top level domain)
  • 제일 하위에 authoritative DNS servers

과정

    1. 로컬 네임 서버에 물어봐, 캐시되어 있으면 알게되고 없으면
    1. Root한테 물어보고 root는 TLD 정보 알려준다
    1. TLD가 난 모르는데(캐시 없으면) 얘에 관한 정보 알고 있는 애 알려줄게 한다. (authoritative dns server 알려준다)
    1. authoriative dns server는 답을 제공한다.
  • 사용자 입장에서는 로컬 네임 서버한테 물어보고 로컬 네임 서버가 답해준 것이지만 그 로컬 네임 서버는 이런 저런 작업을 한 것

인터넷 연결

  • 인터넷 클라이언트와 서버는 연결 connection을 통해서 바이트 스트림을 주고받는 방식으로 통신한다.
  • 이 연결은 두개의 프로세스를 연결한다는 점에서 point-to-point 연결이다.
  • 데이터가 동시에 양방향으로 흐를 수 있다는 의미에서 이것은 완전양방향full-duplex이다.
  • 그리고 소스 프로세스가 보낸 바이트 스트림이 결국은 보낸 것과 동일한 순서로 목적지 프로세스에서 수신된다는 의미에서 안정적이다

소켓은 연결의 종단점

소켓socket은 연결의 종단점이다.

IP + port

각 소켓은 인터넷 주소와 16비트 정수 포트으로 이루어진 소켓 주소를 가지며, 이것은 address : port로 나타낸다.

  • 클라이언트 소켓 주소는 임시 할당하고
  • 서버의 소켓 주소에 있는 포트는 대개 영구적으로 이 서비스에 연결되는 잘 알려진 포트를 사용한다

함수

open_listenfd(포트번호)

int open_listenfd(char *port) 
{
    struct addrinfo hints, *listp, *p;
    int listenfd, rc, optval=1;

    /* Get a list of potential server addresses */
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;             /* Accept connections */
    hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... on any IP address */
    hints.ai_flags |= AI_NUMERICSERV;            /* ... using port number */
    if ((rc = getaddrinfo(NULL, port, &hints, &listp)) != 0) {
        fprintf(stderr, "getaddrinfo failed (port %s): %s\n", port, gai_strerror(rc));
        return -2;
    }

    /* Walk the list for one that we can bind to */
    for (p = listp; p; p = p->ai_next) {
        /* Create a socket descriptor */
        if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 
            continue;  /* Socket failed, try the next */

        /* Eliminates "Address already in use" error from bind */
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,    //line:netp:csapp:setsockopt
                   (const void *)&optval , sizeof(int));

        /* Bind the descriptor to the address */
        if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
            break; /* Success */
        if (close(listenfd) < 0) { /* Bind failed, try the next */
            fprintf(stderr, "open_listenfd close failed: %s\n", strerror(errno));
            return -1;
        }
    }


    /* Clean up */
    freeaddrinfo(listp);
    if (!p) /* No address worked */
        return -1;

    /* Make it a listening socket ready to accept connection requests */
    if (listen(listenfd, LISTENQ) < 0) {
        close(listenfd);
	return -1;
    }
    return listenfd;
}
  • 포트에 연결 요청을 받을 준비가 된 듣기 식별자를 return(p909)
  • host를 NULL, 인자로 받은 포트 번호 hints를 인자로 getaddrinfo()에 넣어 실행하면 &listp에 (listen_fd)가 반환된다.
    • 클라이언트가 getaddrinfo()를 호출할 때는 접속하려는 상대(서버)의 주소를 넣어서 getaddrinfo()로 어드레스 구조체를 획득했다. (이때는 getaddrinfo가 도메인으로 ip를 찾기도 하는 것 같다(dns이용))
    • 서버 측에서는 null로 해서 모든 곳으로부터 요청을 듣겠다고 하고 ai_flags에 AI_PASSIVE라는 것을 넣어 수동적인 소켓을 생성하겟다고 하는 것.

open_clientfd.Getaddrinfo, open_listenfd.Getaddrinfo

int open_clientfd(char *hostname, char *port) {
    int clientfd, rc;
    struct addrinfo hints, *listp, *p;

    /* Get a list of potential server addresses */
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_socktype = SOCK_STREAM;  /* Open a connection */
    hints.ai_flags = AI_NUMERICSERV;  /* ... using a numeric port arg. */
    hints.ai_flags |= AI_ADDRCONFIG;  /* Recommended for connections */
    if ((rc = getaddrinfo(hostname, port, &hints, &listp)) != 0) {
        fprintf(stderr, "getaddrinfo failed (%s:%s): %s\n", hostname, port, gai_strerror(rc));
        return -2;
    }
  
    /* Walk the list for one that we can successfully connect to */
    for (p = listp; p; p = p->ai_next) {
        /* Create a socket descriptor */
        if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) 
            continue; /* Socket failed, try the next */

        /* Connect to the server */
        if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1) 
            break; /* Success */
        if (close(clientfd) < 0) { /* Connect failed, try another */  //line:netp:openclientfd:closefd
            fprintf(stderr, "open_clientfd: close failed: %s\n", strerror(errno));
            return -1;
        } 
    } 

    /* Clean up */
    freeaddrinfo(listp);
    if (!p) /* All connects failed */
        return -1;
    else    /* The last connect succeeded */
        return clientfd;
}
  • open_clientfd를 보면 Getaddrinfo()의 접속하고자 하는 (open_clientfd의 인자로 받은) hostname과 port를 인자로 addrinfo 구조체 연결리스트를 가리키는 포인터를 return
    • 해당 hostname과 port에 해당하는 최대 3개의 노드를 가진 연결리스트를 순회하면서 connect를 시도한다.
  • open_listenfd를 보면 getaddrinfo(NULL, port, &hints, &listp)) 에 NULL과 hints에 passive를 넣어 모든 주소로부터 요청을 받는 소켓을 받는다. 이 때는 구조체 연결리스트를 순회하면서 socket()과 bind()를 시도한다.
  • getaddrinfo로 얻은 addrinfo 구조체에는 서버의 주소가 저장될 것이다. 클라이언트 입장에서 인자로 넣은 서버의 값, 서버 입장에서는 자신의 서버와 포트번호 값, 그렇게 해서 그 서버의 주소가 클라이언트의 connect()에서는 연결 대상으로, 서버의 bind()에서는 소켓과 bind할 주소로 사용된다. b3ab89523f7e/image.png)

프록시란

HTTP 완벽 가이드

  • 클라이언트와 서버 사이에 위치한 HTTP 중개자
  • 클라이언트와 서버 사이에 위치해서 클라이언트의 모든 HTTP 요청을 받아서 서버에 전달(대개 요청 수정 뒤 전달)
  • 사용자 대신해 서버에 접근
  • 프록시는 주로 보안을 위해 사용(신뢰할만한 중개자)
  • 프록시는 요청과 응답을 필터링(바이러스 검출 등)

virtual hosting과 host 헤더

http 완벽가이드 참고

  • 한 개의 서버에 여러 사이트들을 호스트
  • 그러니까, 하나의 IP에 WWW.NAVER.COM, www.daum.net이 호스팅 되고 있을 때 HTTP/1.1 명에에서는 GET /index.html 요청으로 가니까 두 사이트에 대한 각각의 요청이 똑같은 요청으로 가게 된다.
  • 서버가 여러 개의 사이트를 가상 호스팅 하고 있으면, 사용자가 어떤 가상 웹 사이트로 접근하려고 하는 것인지 아는데 정보가 충분하지 않은 것
  • HTTP/1.1은 이 문제를 해결하기 위해 Host 헤더를 지원한다

URL 경로를 통한 가상 호스팅

  • www.naver.com/naver/index.html과 www.daum.net/daum/index.html으로 분리
  • URL 지저분 해진다

포트번호를 통한 가상 호스팅

  • 포트번호를 다르게 할 당
  • 문제 사용자는 URL에 비표준 포트를 쓰지 않고서도 리소스를 찾길 원한다 (80포트 생략)

IP 주소를 통한 가상 호스팅

  • 가상 서버의 각 IP -> 공용 서버에 연결
  • 문제, 컴퓨터 시스템이 연결할 수 있는 장비의 IP 수는 제한
  • IP는 희소상품이라는 문제
  • 서버를 복제까지 해야 하면 더 IP 할당해야 하는 심각한 문제

Host 헤더를 통한 가상 호스팅

  • 원 호스트 명을 받게 하도록 http 확장
  • 모든 요청에 호스트명 포트를 Host 확장 헤더에 기술해서 전달한다.
  • HTTP/1.1 명세 따르려면 Host 헤더를 반드시 기술해야 한다
  • Host헤더는 RFC 2068에 정의되어 있는 HTTP/1.1 요청 헤더
    Host = "Host" ":" 호스트[":"포트] 형식

쓰레드

https://velog.io/@bongf/study-java-whiteship-javaStudy-week10

  • 쓰레드는 최소 실행 흐름
  • 한 프로세스에 쓰레드 여러 개
  • 실제로 동시에 돌아가는 쓰레드 수는 CPU 코어(core)의 수다.
  • 다만, 하나의 코어 하나의 프로세스 내에서는 짧은 시간 동안 코어가 작업을 이 쓰레드, 저 쓰레드 작업을 수행하며 동시에 일어나는 것처럼 보인다.

Fork는 프로세스 복제, Thread는 쓰레드를 생성하는 것

fork()

  • fork()가 언제쓰이나 고민했었는데 이렇게 프로그램 실행 도중 다른 프로그램을 실행해야 할 때 fork를 하는 것을 알게 되었다.
    • 자바에서는 다른 프로그램 실해이 아니라 클래스로 만들로 그를 호출할 것 같은데 fork하면 자원 낭비가 아닌지 의문이 들었다

detach()

  • 쓰레드가 종료되어도 자원을 반납하지 않아서 보통자식 끝날 때까지 기다렸다가 자식프로세스랑 join하는데 join 할 필요 없는 애를 애초에 detach 한다
  • detach()하면 쓰레드 끝나면 자원을 반환한다.

CGI

  • CGI : Interface인데 웹서버와 - 요청을 받아 처리해 줄 로직을 담고 있는 애플리케이션 프로그램 사이의 인터페이스
  • CGI 불편해서 서블렛 나옴
profile
spring, java학습

0개의 댓글

관련 채용 정보