[WEEK07] 네트워크 & TCP/IP & OSI 7 layers

novxerim·2021년 12월 24일
0

SW-Jungle

목록 보기
33/59

TCP/IP

Client/Server model

코치님 설명

네트워크상에서 커넥션을 기다리는 쪽이 서버. 요청을 서빙한다해서 서버.


1) 서버 쪽의 프로세스는 Socket으로 file discription을 받고 bind로 어디에서 기다릴건지 요청을 한다.
2) 기다리는 작업 시작하고, 실제로 커넥션 오게되는걸 받는 세트를 한다.
커넥션이 안오면 blocking이라고해서 잠을 잔다. 서게 되고

1) 클라이언트 입장에서는 소켓을 통해 file discription을 받고 커넥트를 통해 서버 접속을 요청한다.
2) connect받게되면 accept가 풀린다.
(socket fd와 accept가 받는 fd가 다르다. socket자체는 서버 클라가 같이 쓰는데 서버는 bind, listen을 쓰고, 클라에서는 connect를 쓴다.)
accept는 상대방이 connect를 요청했을 때 accept가 풀리면서 fd를 리턴하는 것.
3) read/write를 쭉 하다가 끝나면 close.


  • SSH : cpu socket, 22번포트.

  • 서버가 import해서 기다리고 있겠다고 하는 것이 bind.
    이 과정을 왜하냐면, 다른 프로그램이 같은 포트로 기다리고 있을 수도 있기 때문이다. 그 경우 bind에서 거절한다.

  • 서버쪽에 port있고, 클라쪽에도 port있음 랜덤으로.

  • 웹서버는 80포트

  • 443포트 : TLS 위에서.. TCP위의 secure 서큘레이어라고 불리던 tranport 시큐어러티 레이어 이던 TUS?라고 불리던 것. 위에서 암호프로토콜이 돌아가는 상태에서.


[컴퓨터 시스템 교재 그림(소켓 인터페이스)]

이 그림은 위에서 본 그림과 약간 다르지만, 구조는 같다.
일반적으로 리눅스에서 file open, read write close하는데
open에 해당되는 것을 대신해주는게 socket API이다.
기본적으로는 리눅스의 I/O를 사용해서 네크워크에 커넥션을 한다.


<OSI 7 Layer>

  • 사용 계층의 하위 계층은 신경을 안씀.
  • UDP(User Datagram Protocol) : 영상 스트리밍에 많이 사용.
  • TCP(Transmission Control Protocol) : 연결 개념이 있음. 메시지를 보낼 때 순서가 있다. 메시지 전달에 책임. 대체로 느림. 순서 뒤집히면 안됨
  • IP(Internet Protocol) : 메시지를 보내지만, 받든지 말든지. 네트워크 순서 뒤집힐 수 있음.
  • RTCP : real type tranfer control protocol 정확하게 전달되어야 함
  • RTP : real type tranfer protocol

TCP/IP는 우리가 건들지 않음(구현X). 소켓 인터페이스를 사용하는 것. 커널 안에 들어가있음.
결국 이 인터페이스를 가지고 HTTP1.0를 구현하는 것. 현재 HTTP3.0까지 있음.

네트워크(IP)(인터넷 프로토콜 어드레스) 프로토콜의 목적

코치님 설명

구분해야할건 노드이다. 컴퓨터와 컴퓨터를 구분해서 이 컴퓨터에서 저 컴퓨터로 가는 것.
이 컴퓨터는 어떤 ID(IP)를 가지고, 저 컴퓨터는 어떤 ID(IP)를 가지는지.
이 프로토콜의 역할은 어떤 노드를 TCP프로토콜에서는 전송을 목적으로 하는데
어떤 프로그램에서 어떤 프로그램으로 전송을 하는지. 그 프로그램을 구분하는게 포트이다.
IP address는 컴퓨터를 규정하는거고, 포트는 어느 프로그램인지를 규정하게 된다.


System call 보기

코드를 구현하는 도중 알 수 없는 시스템 콜 함수들이 많이 있었는데, 그 시스템콜에 대한 정보를 보는 것은 아주 간단했다.
예를들어 Open() 이라는 코드가 쓰여있는데 Open이라는 시스템콜이 뭔지 모르겠을 때, Linux(터미널)에서 man 2 open 이란 명령을 실행해보면 현재 설치된 Linux에서 제공하는 flag들을 알 수 있다.
ex) man 2 open / man 2 mmap


컴퓨터시스템

11-6 D 정답 '헤더가 갖는 의미들'

영어로 되어있어서 대충 파파고 돌림 ㅎㅎ
Accept: 14.1
The Accept request-header field can be used to specify certain media types which are acceptable for the response. Accept headers can be used to indicate that the request is specifically limited to a small set of desired types, as in the case of a request for an in-line image.
수락: 14.1
Accept request-header 필드는 응답에 허용되는 특정 미디어 유형을 지정하는 데 사용할 수 있습니다. 수락 헤더는 인라인 이미지에 대한 요청의 경우처럼, 요청이 원하는 유형의 작은 집합으로 특별히 제한됨을 나타내는 데 사용할 수 있습니다.

Accept-Encoding: 14.3
The Accept-Encoding request-header field is similar to Accept, but restricts the content-codings that are acceptable in the response.
인코딩 수락: 14.3
Accept-Encoding 요청 헤더 필드는 Accept와 유사하지만 응답에서 허용되는 콘텐츠 코딩을 제한합니다.

Accept-Language: 14.4
The Accept-Language request-header field is similar to Accept, but restricts the set of natural languages that are preferred as a response to the request. Language tags are defined in section 3.10.
허용 언어: 14.4
Accept-Language 요청 헤더 필드는 Accept와 유사하지만 요청에 대한 응답으로 선호되는 자연어 집합을 제한합니다. 언어 태그는 섹션 3.10에 정의되어 있습니다.

Connection: 14.10
The Connection general-header field allows the sender to specify options that are desired for that particular connection and MUST NOT be communicated by proxies over further connections.
연결: 14.10
연결 일반 헤더 필드를 사용하면 발신자가 해당 특정 연결에 필요한 옵션을 지정할 수 있으며 추가 연결을 통해 프록시에 의해 통신되어서는 안 됩니다(MUST NOT).

Host: 14.23
The Host request-header field specifies the Internet host and port number of the resource being requested, as obtained from the original URI given by the user or referring resource. The Host field value MUST represent the naming authority of the origin server or gateway given by the original URL. This allows the origin server or gateway to differentiate between internally-ambiguous URLs, such as the root “/” URL of a server for multiple host names on a single IP address.
호스트: 14.23
호스트 요청 헤더 필드는 사용자 또는 참조 리소스가 제공한 원래 URI에서 얻은 인터넷 호스트 및 요청 중인 리소스의 포트 번호를 지정합니다. 호스트 필드 값은 원본 URL에서 제공한 원본 서버 또는 게이트웨이의 명명 권한을 나타내야 합니다(MUST). 이를 통해 원본 서버 또는 게이트웨이는 단일 IP 주소에 있는 여러 호스트 이름에 대한 서버의 루트 "/" URL과 같이 내부적으로 모호한 URL을 구별할 수 있습니다.

User-Agent: 14.43
The User-Agent request-header field contains information about the user agent originating the request. This is for statistical purposes, the tracing of protocol violations, and automated recognition of user agents for the sake of tailoring responses to avoid particular user agent limitations. User agents SHOULD include this field with requests. The field can contain multiple product tokens (section 3.8) and comments identifying the agent and any subproducts which form a significant part of the user agent. By convention, the product tokens are listed in order of their significance for identifying the application.
사용자 에이전트: 14.43
User-Agent request-header 필드는 요청을 시작한 사용자 에이전트에 대한 정보를 포함합니다. 이는 통계적 목적, 프로토콜 위반 추적 및 특정 사용자 에이전트 제한을 피하기 위해 응답을 조정하기 위한 사용자 에이전트의 자동 인식을 위한 것입니다. 사용자 에이전트는 요청과 함께 이 필드를 포함해야 합니다(SHOULD). 이 필드는 여러 제품 토큰(섹션 3.8)과 사용자 에이전트의 중요한 부분을 구성하는 에이전트 및 하위 제품을 식별하는 설명을 포함할 수 있습니다. 관례에 따라 제품 토큰은 애플리케이션을 식별하는 데 중요한 순서대로 나열됩니다.

HTTP 1.0 / 1.1 차이

HTTP 1.1 : 지속됨, 여러개도 주고받기 가능
참고 블로그 그림 참고하기 : https://withbundo.blogspot.com/2021/02/http-http-10-http-11.html

HTTP 를 이용한 데이터 전달은 TCP 세션 기반에서 이루어 진다.
HTTP 1.0 과 1.1 의 차이는 TCP 세션을 지속적으로 유지할 수 있느냐? 없느냐에 차이를 둔다


mmap 파라미터

에 관한 글
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=scm88&logNo=221823657725


11.7 비디오 삽입 에러

Rio_writen error: Connection reset by peer 에러

-> 동영상을 주석처리하고 일단 넘어갔다.
아무래도 동영상을 출력하고 뭔가가 남아있거나, 용량을 서버에서 감당 못하거나? 근데 44초에 10메가짜리였는데. 모르겠다.


11.11 과제

Telnet 실행 (client)

tiny 서버 키고 (proxy 켜져있으면 안됨)
다른 터미널에서 tiny폴더에서
telnet IP주소 포트주소
클라이언트로써 접속
결과

Trying 15.164.94.35...
Connected to 15.164.94.35.
Escape character is '^]'.

그 다음

1) doit 함수에 GET 거르는 함수 옆에 HEAD 조건도 추가해준다
2) serve_static, serve_dynamic 함수 안에 if문 추가해준다

doit

void doit(int fd) { /* 한 개의 HTTP 트랜잭션을 처리 */
  int is_static; 
  struct stat sbuf;
  char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
  char filename[MAXLINE], cgiargs[MAXLINE];
  rio_t rio; // rio_readlineb를 위해 rio_t 타입(구조체)의 읽기 버퍼를 선언

  /* Read request line and headers */  
  /* Rio = Robust I/O */
  // rio_t 구조체를 초기화 해준다.
  Rio_readinitb(&rio, fd); // 요청 라인 읽어들임(컴터시스템 p.921-4line) // rio버퍼와 fd. 서버의 connfd를 연결시킨다
  Rio_readlineb(&rio, buf, MAXLINE); // 요청 라인 읽고 분석, rio에 있는 string을 버퍼로 다 옮긴다
  printf("Request headers:\n");
  printf("%s", buf); // 우리가 요청한 자료를 표준 출력 해준다 (godzilla)
  sscanf(buf, "%s %s %s", method, uri, version);
  printf("Get image file uri : %s\n", uri); // 추가코드
      // 같은 문자가 아닐 때 조건문   // GET이거나 HEAD도 아닐 때 /* 숙제 11.11 */
  if (strcasecmp(method, "GET") && strcasecmp(method,  "HEAD")) { // 같으면 0반환이라 if문 안들어감 1은 true라 에러실행 // Tiny는 GET메소드만 지원. 만약 다른 메소드(like POST)를 요청하면. strcasecmp : 문자열비교.
    clienterror(fd, method, "501", "Not implemented", "Tiny does not implement this method"); // 에러메시지 보내고, main루틴으로 돌아온다
    return; // 그 후 연결 닫고 다음 요청 기다림. 그렇지 않으면 읽어들이고
  }

  read_requesthdrs(&rio); // GET method라면(0) 그건 읽고, 다른 요청 헤더들은 무시 (그냥 프린트한다)

  /* Parse URI from GET request */ // uri 받은거 분석하는데 이게 정적 컨텐츠이면 1이 나옴 -> 밑에 2번째 if문으로 들감
  is_static = parse_uri(uri, filename, cgiargs); // URI를 파일 이름과, 비어있을 수도 있는 CGI인자 스트링 분석하고 // 정적이면 1 // 파일네임 여기서 prase_uri를 통해 만들어냄 (매개변수처럼..담는 그릇)
  if (stat(filename, &sbuf) < 0) { // 요청이 정적 또는 동적 컨텐츠를 위한 것인지 나타내는 플래그 설정 // 버퍼에 파일네임을 넘긴다
    clienterror(fd, filename, "404", "Not found", "Tiny couldn't find this file"); 
    // 만일 파일이 이 디스크 상에 있지 않으면, 에러메시지를 즉시 클라이언트에게 보내고 리턴.
    return;
  }

  if (is_static) { /* Serve static content */ // 만일 요청이 정적 컨텐츠를 위한 것이라면
    if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { // 이 파일이 보통 파일이라는 것과 읽기 권한을 갖고 있는지를 검증한다. 맞으면1, 아니면0
      // sbuf.st_mode : sbuf의 내용 중 st_mode의 값(어떤 타입의 파일인지 16바이트로 나옴)
      clienterror(fd, filename, "403", "Forbidden", "Tiny couldn't read the file");
      return;
    }
    serve_static(fd, filename, sbuf.st_size, method); // 만일 맞다면 정적 컨텐츠를 클라이언트에게 제공
  }
  else { /* Serve dynamic content */
    if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) { // 만일 요청이 동적 컨텐츠에 대한 것이라면 이 파일이 실행 가능한지 검증하고
      clienterror(fd, filename, "403", "Forbidden", "Tiny couldn't run the CGI program"); 
      return;
    }
    serve_dynamic(fd, filename, cgiargs, method); // 만일 그렇다면 진행해서 동적 컨텐츠를 클라이언트에게 제공한다
  }  
}

serve_static

/* 지역 파일의 내용을 포함하고 있는 본체를 갖는 HTTP 응답을 보낸다 */
// 클라이언트가 원하는 파일 디렉토리를 받아온다. 응답 라인과 헤더를 작성하고 서버에게 보낸다. serve~
void serve_static(int fd, char *filename, int filesize, char *method) {
  int srcfd;
  char *srcp, filetype[MAXLINE], buf[MAXBUF], *fbuf;

  /* Send response headers to client */
  get_filetype(filename, filetype); // 파일 이름의 접미어 부분 검사해서 파일 타입 결정
  sprintf(buf, "HTTP/1.0 200 OK\r\n"); // 클라이언트에 응답 줄과 응답 헤더를 보낸다 // 버퍼에 넣고 출력
  sprintf(buf, "%sServer: Tiny Web Server\r\n", buf);
  sprintf(buf, "%sConnection: close\r\n", buf);
  sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
  sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype); // 빈 줄 한 개가 헤더를 종료하고 있음
  /* writen = client(텔넷)에서 출력됨*/
  Rio_writen(fd, buf, strlen(buf)); // 요청한 파일의 내용을 연결 식별자 fd로 복사해서 응답 본체를 보낸다 // 버퍼를 옮김
  /* 서버 쪽에 출력 */
  printf("Response headers:\n"); 
  printf("%s", buf);

  if (!strcasecmp(method, "HEAD")) // 같으면 0(false) 들어가고 끝냄(HEAD가 맞으면)
    return; // void 타입이라 바로 리턴해도 됨(끝내라)

  /* Send response body to client */
  srcfd = Open(filename, O_RDONLY, 0); // 열려고 하는 파일의 식별자 번호 리턴. filename을 오픈하고 식별자를 얻어온다
                                // ㄴ 0 : 읽기 전용이기도하고, 새로 파일을 만드는게 아니니 Null처럼 없다는 의미..없어도 됨
  // srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0); // mmap : 요청한 파일을 가상메모리 영역으로 매핑한다 / 커널 내부 주소 접근 시에 사용
  // // mmap 호출시 위에서 받아온 모든 요청 정보들(srcfd)을 전부 매핑해서 srcp로 받는다(포인터)
  // Close(srcfd); // srcfd 내용을 메모리로 매핑한 후에 더 이상 이 식별자 필요X, 파일을 닫는다. 안 닫으면 메모리 누수 치명적
  // Rio_writen(fd, srcp, filesize); // 실제로 파일을 클라이언트에게 전송. // srcp내용을 fd에 filesize만큼 복사해서 넣는다
  // Munmap(srcp, filesize); // 매핑된 srcp 주소를 반환한다. 치명적인 메모리 누수를 피한다 

  // // mmap-munmap은 malloc-free처럼 세트 / malloc()은, 큰 메모리 블럭 요청이 들어오면, 내부적으로 mmap()을 써서 메모리를 할당합니다. 포함은 X
  // mmap()은 시스템에서 제공하는 저수준 시스템 콜이며, 특별한 조건일 때, (malloc과 유사하게) 메모리를 할당하는 효과를 볼 수 있습니다. 
  // malloc()은 메모리를 할당하는 C library function이며, 내부적으로 mmap(), brk() 등의 시스템 콜을 써서 구현될 수 있습니다.
  // mmap 공간을 잡는 동시에 내용을 넣음 free시 시스템에 즉각 반영

  /* 숙제문제 11.9 */ // 실행시 위에 srcp부터 ~ Munmap 까지 주석처리 할 것 // 빈공간 할당하고 내용 넣음(Read를 해줘야함) free시 시스템에 즉각반영되지 않음
  fbuf = malloc(filesize); //filesize 만큼의 가상 메모리(힙)를 할당한 후(malloc은 아무것도 없는 빈 상태에서 시작) , Rio_readn 으로 할당된 가상 메모리 공간의 시작점인 fbuf를 기준으로 srcfd 파일을 읽어 복사해넣는다.
  Rio_readn(srcfd, fbuf, filesize); // srcfd 내용을 fbuf에 넣는다(버퍼에 채워줌)
  Close(srcfd); // 윗줄 실행 후 필요 없어져서 닫아준다 // 양 쪽 모두 생성한 파일 식별자 번호인 srcfd 를 Close() 해주고
  Rio_writen(fd, fbuf, filesize); // Rio_writen 함수 (시스템 콜) 을 통해 클라이언트에게 전송한다 
  // fbuf를 fd에다가 넣는다(fbuf는 사실 포인터. 걔를 밀면서 writen, 미는 애는 새로 선언한 usrbuf)
  free(fbuf); // Mmap은 Munmap, malloc은 free로 할당된 가상 메모리를 해제해준다.
}

serve_dynamic

/* Tiny는 자식 프로세스 fork후, CGI프로그램을 자식의 컨텍스트에서 실행하며 모든 종류의 동적 컨텐츠를 제공함 */
void serve_dynamic(int fd, char *filename, char *cgiargs, char *method) {
  char buf[MAXLINE], *emptylist[] = { NULL };

  /* Return first part of HTTP response */
  // 클라이언트에게 성공을 알려주는 응답 라인을 보내는 것으로 시작
  sprintf(buf, "HTTP/1.0 200 OK\r\n"); 
  Rio_writen(fd, buf, strlen(buf));  // fd에 버퍼 넣는다
  sprintf(buf, "Server: Tiny Web Server\r\n");
  Rio_writen(fd, buf, strlen(buf));

  if (!strcasecmp(method, "HEAD")) // 같으면 0(false) 들어가고 끝냄(HEAD가 맞으면)
    return; // void 타입이라 바로 리턴해도 됨(끝내라)

  // 응답의 첫 번째 부분을 보낸 후, 새로운 자식 프로세스를 fork한다
  if (Fork() == 0) { /* Child */ // 0 : 정삭적으로 fork됨. 함수보면 <0이면 에러임
    /* Real server would set all CGI vars here */

    // cgiargs에 arguments 2개가 들어있음 (parse에서 물음표 기준으로 2개로 나눴음 strcpy)
    /* 예시
    uri : /cgi-bin/adder?123&123
    ->
    cgiargs : 123&123
    filename : ./cgi-bin/adder */

    // fork한 곳에서 set env 바꿔줌
    setenv("QUERY_STRING", cgiargs, 1); // 환경변수 설정. QUERY_STRING이라는 환경변수에 요청 URI의 CGI인자들(cgiargs)로 설정해준다. 1 : 우선순위 순서
    Dup2(fd, STDOUT_FILENO); /* Redirect stdout to client */ // 자식의 표준 출력을 연결 파일 식별자로 재지정 // fd를 STDOUT자리로 복사(자식에게 복사)
    Execve(filename, emptylist, environ); /* Run CGI program */ // 그 후 CGI프로그램 로드 후 실행
  }
  Wait(NULL); /* Parent waits for and reaps child */ // 부모는 자식이 종료되어 정리되는 것을 기다리기 위해 wait 함수에서 블록된다(대기하는 함수)
}

tiny 서버 실행 시키고 (proxy 켜져있으면 안됨)
다른 터미널 tiny폴더에서
telnet 15.164.94.35 8000
클라이언트로써 접속
결과
Trying 15.164.94.35...
Connected to 15.164.94.35.
Escape character is '^]'.

GET / HTTP/1.0
엔터 2번
리눅스(서버) : 서버가 받는 헤더 정보 출력 (요청받은거 띄움)
텔넷(클라이언트) : 클라이언트가 받는 내용 (응답받은거 띄움)


HEAD / HTTP/1.0
=> 클라에서 헤더값만 깔끔하게 띄움 (serve_static) 참고

왜 if문으로 거르냐면
serve_static함수 안에서 Rio_writen(fd, buf, strlen(buf)); // 요청한 파일의 내용을 연결 식별자 fd로 복사해서 응답 본체를 보낸다 // 버퍼를 옮김
줄에서 클라이언트(텔넷)쪽에 출력을 하는데
그 밑에서 if (!strcasecmp(method, "HEAD")) // 같으면 0(false). 다를 때 if문 안으로 들어감
return; // void 타입이라 바로 리턴해도 됨(끝내라)
이걸로 헤드가 들어왔을때 더 출력 안하게 끝내줌
그러면 그 밑에 response body(Open~mmap/malloc~~ close)는 더 출력을 안함



참고사이트

profile
블로그 이전했습니다. https://yerimi11.tistory.com/

0개의 댓글