[WebProxy] Tiny 서버 숙제 문제 분석 및 해결하기

2025. 5. 3. 23:25·크래프톤 정글/Code 정글(C언어)

Tiny 서버 숙제 문제 분석 및 해결하기

Tiny 서버의 기본 구현을 완료한 뒤에는, 몇 가지 추가적인 숙제들을 해결해야 하는데요. 이번 글에서는 이 숙제들이 구체적으로 무엇을 요구하는지, 또 이를 만든 사람들이 학생들이 직접 해결하면서 어떤 개념이나 경험을 얻기를 바라는지 하나씩 분석해보겠습니다.

 

다만 깊이 있는 학습 경험을 위해 각 숙제에 대해 곧바로 학습 목표나 해결 방법을 확인하기보다는, 우선 문제 설명만 읽은 뒤 직접 해결 방안을 고민하고 적용해보는 과정을 거친 후, 이를 검토하거나 보완하는 용도로 학습 목표나 해결 방법을 활용하시는 것을 추천드립니다.

 

숙제 11.6C

"TINY의 출력을 조사해서 사용하는 브라우저의 HTTP 버전 결정하기"

 

문제 설명

Tiny 서버의 터미널 출력을 통해, 브라우저가 서버에 어떤 HTTP 버전으로 요청을 보내는지 알아보기

 

학습 목표

  • HTTP 요청 메시지 형식의 구조 이해
    • 요청 메서드, URI, 프로토콜 버전으로 구성된 요청 라인을 직접 확인하기
    • 이를 통해 이론에서 배운 HTTP 메시지 형식을 실제로 체험하기
  • 클라이언트(브라우저)와 서버 간 통신의 실체 파악
    • 우리 평소에 아무 생각 없이 켜는 브라우저가, 실제로는 서버와 어떤 방식으로 통신하고 있는지를 눈으로 확인하기
  • HTTP 프로토콜의 발전과 하위 호환성에 대한 관심 유도
    • 브라우저가 보통 HTTP/1.1을 쓰지만, HTTP/2나 HTTP/3는 어떤 차이가 있는지 관심 가져보기
    • Tiny는 HTTP/1.0만 지원하므로, 클라이언트는 하위 버전인 서버에도 맞춰서 요청을 보낼 수 있다는 점 알기

 

해결 방법

Tiny 서버는 클라이언트 요청을 처리하는 핵심 함수인 doit() 내부에서, 요청 라인을 터미널에 그대로 출력하고 있는데요. 이 부분은 다음과 같은 구현되어 있습니다.

void doit(int fd) {
    ...
    Rio_readlineb(&rio, buf, MAXLINE); // 요청 라인 읽기
    printf("Request headers:\n");
    printf("%s", buf); // 요청 라인을 터미널에 출력
    ...
}

따라서 Tiny 서버 실행 후, 웹 브라우저에서 Tiny에 접속하면 터미널에 다음과 같은 출력이 보일 것입니다.

Request headers:
GET /home.html HTTP/1.1

여기서 마지막 부분인 HTTP/1.1이 브라우저가 사용하는 HTTP 버전입니다.

 

참고로 최근 브라우저는 HTTP/2 이상을 기본으로 시도하지만, Tiny처럼 HTTP/1.0만 지원하는 서버에 대해서는 자동으로 HTTP/1.1 같은 하위 버전으로 요청을 보냅니다.

 

 

숙제 11.7

"TINY를 확장해 MPG 비디오 파일을 처리하고, 실제 브라우저를 사용해 결과 체크하기"

-> 최신 브라우저에서는 MPG 파일 재생을 지원하지 않으므로 MP4로 대체해서 진행하기

 

문제 설명

Tiny는 기본적으로 HTML, GIF, JPEG 등 몇 가지 파일 확장자에 대한 MIME 타입만 처리하고 있는데요. 이 과제는 여기에 .mp4 파일에 대한 MIME 타입 추가를 통해, 비디오 파일도 클라이언트에게 올바르게 전송되도록 Tiny를 확장하는 것이 핵심입니다.

 

그리고 서버가 이를 올바르게 응답하고 있는지를 브라우저에서 실제로 확인하는 것까지가 포함된 과제입니다.

 

학습 목표

  • MIME 타입의 역할과 중요성 이해
    • 파일 확장자에 따라 웹 서버가 올바른 콘텐츠 타입을 명시해줘야 브라우저가 그에 맞는 방식으로 해석할 수 있다는 것을 경험하기
  • 서버 코드의 확장성 체험
    • 정적 콘텐츠 종류가 다양해지는 상황에서, 서버가 어떻게 확장될 수 있는지 직접 수정해보며 감 잡기
    • 작지만 실제 웹 서버처럼 다뤄보는 경험
  • 브라우저 동작과 응답 처리 방식 이해
    • 브라우저가 Content-Type 헤더에 따라 어떻게 콘텐츠를 다르게 렌더링하는지를 직접 확인하기
  • 텍스트 기반과 바이너리 기반 콘텐츠의 차이 체감
    • HTML, 이미지, 동영상 등 콘텐츠의 종류에 따라 서버-클라이언트 간 처리 방식이 어떻게 달라지는지 체험하기

 

해결 방법

(1) get_filetype() 함수에 .mp4 추가하기

else if (strstr(filename, ".mpg")) {
    strcpy(filetype, "video/mpeg");
}

 

(2) home.html에 <video> 태그를 사용해 .mp4 파일 삽입하기

※ sample mp4 파일은 아래 동영상을 다운로드

sample.mp4
1.01MB

 

sample.mp4 동영상을 Tiny 서버가 있는 디렉터리에 같이 넣습니다.

 

아래는 video 태그를 삽입한 home.html 예시입니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>
        <div><img align="middle" src="godzilla.gif"></div>
        <div>Dave O'Hallaron</div>
        <div>
            <video controls width="320">
                <source src="sample.mp4" type="video/mp4">
                Your browser does not support the video tag.
            </video>
        </div>
    </div>
</body>
</html>

(3) 브라우저에서 직접 확인하기

tiny 서버를 실행해 home.html에 삽입된 비디오가 잘 재생되는지 확인합니다.

동영상을 제공하는 home.html

 

 

숙제 11.9

“TINY를 수정해서 정적 컨텐츠를 처리할 때 요청한 파일을 mmap과 rio_readn 대신에 malloc, rio_readn, rio_writen을 사용해서 연결 식별자에게 복사하기”

 

문제 설명

Tiny의 기본 구현에서는 mmap() 시스템 콜을 사용하여 정적 파일을 메모리에 매핑한 후, write()를 통해 클라이언트에게 그대로 전송하는데요.


이 과제는 이 방식 대신,

  • malloc()으로 버퍼를 직접 동적 할당하고,
  • rio_readn()으로 파일에서 읽고,
  • rio_writen()으로 클라이언트에게 전송

하는 방식으로 바꿀 것을 요구하고 있습니다.

 

학습 목표

  • 메모리 매핑(mmap) vs 일반 파일 I/O 방식의 차이 이해
    • mmap()은 성능상 효율이 좋지만, 버퍼 복사가 없는 대신 시스템 의존성이 크고 예외 처리에 취약
    • 이를 malloc 기반으로 대체하며 두 방식의 장단점을 비교할 수 있게 하기
  • 메모리 관리와 자원 해제의 중요성 체험
    • malloc()으로 동적 할당한 후에는 반드시 free()를 해줘야 하므로, 자원 관리를 명시적으로 해보는 경험
  • rio 패키지의 안전한 입출력 함수 사용 체득
    • rio_readn, rio_writen 같은 robust I/O 함수를 통해 안전하고 버퍼링된 파일 입출력 방식을 직접 다뤄보기
  • 동일 기능을 다른 방식으로 구현해보는 설계력 강화
    • 같은 결과를 만들어내는 여러 구현 방법이 있음을 인식하고, 성능·안정성 측면에서 비교해보는 기회를 제공

 

해결 방법

Tiny의 serve_static() 함수 내부에서 mmap, munmap, write를 쓰는 핵심 로직을 malloc, read, write, free를 쓰는 방식으로 변경해주면 되는데요.

 

기존 수정 전 핵심 코드는 다음과 같습니다.

srcfd = Open(filename, O_RDONLY, 0);
srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
Close(srcfd);
Rio_writen(fd, srcp, filesize);
Munmap(srcp, filesize);

이 부분을 다음과 같이 수정해줍니다.

srcfd = Open(filename, O_RDONLY, 0);
char *buf = malloc(filesize);
if (buf == NULL) {
    unix_error("malloc error");
}
Rio_readn(srcfd, buf, filesize);   // 파일 내용을 읽고
Close(srcfd);
Rio_writen(fd, buf, filesize);     // 클라이언트에게 전송
free(buf);                         // 메모리 해제

 

 

숙제 11.10

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

B. 실제 브라우저를 사용해서 TINY로부터 이 형식을 요청하고, 채운 형식을 TINY에 보내고, adder가 생성한 동적 컨텐츠를 표시하는 방법으로 여러분의 작업을 체크하라"

 

문제 설명

A. 사용자가 두 숫자를 입력할 수 있는 HTML 폼을 만들고, GET 메서드를 통해 /cgi-bin/adder CGI 프로그램에 요청 보내기

B. 실제 웹 브라우저에서 해당 폼을 요청하고, 숫자를 입력해 Tiny 서버를 통해 CGI 결과가 제대로 표시되는지를 확인하기

 

학습 목표

  • HTML <form>과 GET 방식의 연동 구조 이해
    • 사용자가 웹 폼을 통해 입력한 값을 URL 파라미터로 전송하는 과정을 이해하기
    • GET 방식의 작동 원리와 URL에 파라미터가 포함되는 구조 체험
  • CGI 방식의 서버 동작 흐름 익히기
    • 사용자의 요청을 받아 CGI 프로그램이 실행되고, 그 결과를 브라우저에 출력하는 전체 과정을 실습을 통해 이해하기
  • Tiny 서버의 동적 콘텐츠 처리 구조 복습
    • Tiny는 /cgi-bin 경로를 통해 CGI 프로그램을 실행시킨다. 이 요청 흐름을 정확히 이해하고 실제 실행 과정을 경험하기
  • 브라우저와 서버의 실시간 상호작용 직접 확인하기
    • 폼 제출 → 요청 → CGI 실행 → 응답 → 브라우저 출력이라는 일련의 흐름을 눈으로 확인하기

 

해결 방법

(1) adder.html 만들기

다음과 같은 HTML 코드를 가진 adder.html을 만들어 Tiny의 정적 파일 디렉터리에 넣습니다.

<html>
  <head><title>Adder</title></head>
  <body>
    <form action="/cgi-bin/adder" method="GET">
      <input type="text" name="num1"> +
      <input type="text" name="num2">
      <input type="submit" value="Add">
    </form>
  </body>
</html>
  • action="/cgi-bin/adder" → Tiny가 CGI 프로그램으로 실행하도록 지정한다
  • method="GET" → 입력값이 URL의 QUERY_STRING으로 전달된다
  • name="num1"과 name="num2" → CGI에서 환경변수로 전달되어 처리된다

(2) Tiny 서버 실행 후, 브라우저에서 adder.html 요청하기

브라우저 URL에 아래와 같이 입력해 adder.html을 서버로부터 받아옵니다.

http://localhost:8000/adder.html

 

(3) 원하는 두 숫자 입력 후, 결괏값 확인하기

입력된 두 숫자의 합이 CGI 프로그램(adder)으로부터 처리되어, 결괏값을 잘 받아오는지 확인합니다.

adder.html에서 더할 두 숫자 입력하기
Add를 눌러 받아온 결과 화면

 

 

숙제 11.11

"TINY를 확장해서 HTTP HEAD 메소드를 지원하도록 하라. TELNET을 웹 클라이언트로 사용해서 작업 결과를 체크하시오"

 

문제 설명

기존 Tiny는 GET 요청만을 처리할 수 있습니다. 하지만 HEAD 요청은 본문 없이 헤더만 응답하는 또다른 요청 방식이기 때문에, 이를 처리하도록 doit() 함수에 분기 처리를 추가해야 하는데요.

 

또한 위의 작업을 마친 뒤, TELNET을 이용해 직접 HTTP 요청을 입력하고 서버 응답을 수동으로 확인하라는 실습이 포함되어 있습니다.

 

학습 목표

  • HTTP 메서드의 역할과 차이점 이해
    • GET과 HEAD 메서드는 요청 방식이 같지만 응답 내용이 다르다
    • HEAD는 헤더만 받고, 본문은 받지 않는다는 개념을 체득하기
  • 서버 응답 제어 능력 키우기
    • 같은 파일을 요청하더라도, 요청 방식에 따라 본문 전송을 생략하도록 서버를 동작시켜야 한다
  • 텍스트 기반 프로토콜 수동 조작 경험
    • TELNET을 이용해 HTTP 요청을 직접 입력하면서, 고수준 도구 없이 네트워크 프로토콜의 기초를 이해하기
  • 클라이언트-서버 프로토콜 상호작용의 원리 체감
    • 실제 응답 결과(헤더만 출력됨)를 수동으로 분석함으로써, 클라이언트가 어떤 정보를 바탕으로 동작을 결정하는지 파악하기

 

해결 방법

(1) doit() 함수에 HEAD 처리 추가

Tiny의 doit() 함수에서 method를 판별하는 부분을 다음과 같이 수정합니다.

if (!(strcasecmp(method, "GET") == 0 || strcasecmp(method, "HEAD") == 0)) {
    clienterror(fd, method, "501", "Not Implemented", 
                "Tiny does not implement this method");
    return;
}

그리고 serve_static() 함수 내에서 본문 전송 부분을 분기 처리를 통해 HEAD 요청인 경우 본문을 전송하지 않게 막습니다.

// 클라이언트에게 응답 헤더 전송
sprintf(buf, "HTTP/1.0 200 OK\r\n");
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Server: Tiny Web Server\r\n");
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-length: %d\r\n", filesize);
Rio_writen(fd, buf, strlen(buf));
sprintf(buf, "Content-type: %s\r\n\r\n", filetype);
Rio_writen(fd, buf, strlen(buf));

// HEAD 요청인 경우 본문은 전송하지 않음
if (strcasecmp(method, "HEAD") == 0)
    return;

// GET 요청이면 본문 전송
srcfd = Open(filename, O_RDONLY, 0);
srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
Close(srcfd);
Rio_writen(fd, srcp, filesize);
Munmap(srcp, filesize);

 

(2) serve_static()에 매개 변수 추가

기존 serve_static()에서는 method를 따로 받지 않기 때문에 위의 분기 처리를 추가하면 빨간 줄이 생기는데요. 함수 선언과 정의 부분도 수정해 method 인자를 전달하게 바꿔줍니다.

void serve_static(int fd, char *filename, int filesize, char *method)

 

(3) Telnet으로 HEAD 요청 보내기

Tiny 서버 실행 후 새 터미널을 통해 telnet으로 접속합니다.

telnet localhost 8000

이후 다음과 같이 HEAD 요청을 입력합니다. (마지막 줄에 Enter 두 번)

HEAD /godzilla.gif HTTP/1.0
Host: localhost
// 빈 줄 (Enter 두 번 입력)

 

(4) 서버 응답 확인

서버가 정상적으로 HEAD 요청을 처리하고 본문 없이 헤더만 반환하고 있는지 확인합니다.

HEAD 요청을 정상적으로 처리하는 서버의 모습

저작자표시 비영리 변경금지 (새창열림)

'크래프톤 정글 > Code 정글(C언어)' 카테고리의 다른 글

[WebProxy] Bash 스크립트 "cannot execute: required file not found" 에러 해결 방법  (0) 2025.05.05
[WebProxy] 프록시 서버 제대로 알고 시작하기  (0) 2025.05.05
[WebProxy] Tiny 서버 구현 코드 해석 및 응답 확인하기  (0) 2025.05.03
[WebProxy] echo 서버 구현 실습 및 코드 해석하기  (0) 2025.05.02
[WebProxy] VSCode에서 빨간 줄이 뜨는 현상 해결하기  (0) 2025.05.02
'크래프톤 정글/Code 정글(C언어)' 카테고리의 다른 글
  • [WebProxy] Bash 스크립트 "cannot execute: required file not found" 에러 해결 방법
  • [WebProxy] 프록시 서버 제대로 알고 시작하기
  • [WebProxy] Tiny 서버 구현 코드 해석 및 응답 확인하기
  • [WebProxy] echo 서버 구현 실습 및 코드 해석하기
그냥사람_
그냥사람_
IT 관련 포스팅을 합니다. 크래프톤 정글 8기 정경호
  • 그냥사람_
    그냥코딩
    그냥사람_
  • 전체
    오늘
    어제
    • 글 전체보기 N
      • 크래프톤 정글 N
        • 로드 투 정글(입학시험)
        • CS기초(키워드, 개념정리) N
        • 컴퓨터구조(CSAPP)
        • Code 정글(C언어)
        • Equipped in 정글(나만무) N
        • 마이 정글(WIL, 에세이) N
      • 자료구조&알고리즘
        • 자료구조
        • 알고리즘
      • 일상
  • 블로그 메뉴

    • 홈
  • 링크

    • Github
  • hELLO· Designed By정상우.v4.10.3
그냥사람_
[WebProxy] Tiny 서버 숙제 문제 분석 및 해결하기
상단으로

티스토리툴바