cs:app tiny server 실습 상황보고(1)

강민규·2021년 1월 24일
0

참조:
https://github.com/stkang9409/TinyWebServer

현재 상황

연습문제 11. 7 문제 발생

문제 개요
1. 클라이언트 측에서 내가 보낸 비디오 파일을 읽을 수 있도록 하기 위해, URI parse 시 처리할 수 있는 file type에 mp4 형식을 추가하였다. 이로써 응답 헤더에 content type으로 video/mp4를 보내줄 수 있게 되었다.

void get_filetype(char *filename, char *filetype)
{
    if (strstr(filename, ".html"))
        strcpy(filetype, "text/html");
    else if (strstr(filename, ".gif"))
        strcpy(filetype, "image/gif");
    else if (strstr(filename, ".png"))
        strcpy(filetype, "image/png");
    else if (strstr(filename, ".jpg"))
        strcpy(filetype, "image/jpeg");
    else if (strstr(filename, ".mp4"))
        strcpy(filetype, "video/mp4");
    else if (strstr(filename, ".mpeg"))
        strcpy(filetype, "video/mpeg");
    else
        strcpy(filetype, "text/plain");
}
  1. 이후 서버에 7Mb짜리 mp4 영상을 올리고, home.html을 수정하여 동영상을 확인할 수 있게 되었다.

    • 동영상 관련 요청 및 응답 헤더.
    • 브라우저에서 띄운 페이지
    • 처음 브라우저에 띄우면 동영상이 자동적으로 재생되지 않는다.
  2. 그러나 URI에서 직접 동영상 주소를 입력하면 문제가 발생한다.

    • 응답 요청 헤더
    • 브라우저 상 화면
    • 브라우저 상에 동영상이 아예 뜨지를 않는다.
    • 콘솔 창에 확인을 해보면 서버가 그냥 꺼져버리는 것이 문제임을 알 수 있다.
  3. 어디서 서버가 터지는 것일까..

    • 일단 파일을 클라이언트 측으로 보내는 코드를 보자
    srcfd = Open(filename, O_RDONLY, 0);
        //PROT_READ -> 페이지는 읽을 수만 있다.
        // 파일을 어떤 메모리 공간에 대응시키고 첫주소를 리턴
        srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
        Close(srcfd);
        Rio_writen(fd, srcp, filesize);
        //대응시킨 녀석을 풀어준다. 유효하지 않은 메모리로 만듦
        // void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
        // int munmap(void *start, size_t length);
        Munmap(srcp, filesize);
    • rio_writen 관련
    ssize_t rio_writen(int fd, void *usrbuf, size_t n)
    {
        size_t nleft = n;
        ssize_t nwritten;
        char *bufp = usrbuf;
        printf("filesize: %d\n", n);
        while (nleft > 0)
        {
            if ((nwritten = write(fd, bufp, nleft)) <= 0)
            {
                if (errno == EINTR) /* Interrupted by sig handler return */
                    nwritten = 0;   /* and call write() again */
                else
                {
                    printf("end2\n");
                    return -1; /* errno set by write() */
                }
            }
            nleft -= nwritten;
            printf("leftsize: %d\n", nleft);
            bufp += nwritten;
        }
        printf("end1\n");
        return n;
    }

    프린트 해보다 알게된 것

    • 처음 write를 하면 file size가 좀 남는다.
    • rio_writen에 적혀있는대로면 남았을 떄는 반복문으로 다시 들어가는데, 출력이 사진 상의 두 문장이 끝인 것으로 미루어 보아, 두번째로 반복문에 진입했을 때 정상적으로 함수가 종료되지도 않고 write(2) 시스템 콜에서 문제가 발생해서 프로그램이 종료되어버리는 것 같다.
  4. 그래서 while을 if로 바꾸었더니..

    • 잘된다.
    • 나는 파일이 잘려서 파일 상에 쓰이게 되니 잘린 만큼만 재생될 줄 알았는데, 끝까지 재생이 잘 됐다.
    • 그래서 콘솔을 확인해보니..

      그냥 파일을 끝까지 다 썼다.
  5. 현재 코드 그대로 홈페이지에서는 어떻게 되는가 확인을 해봤다.

    • 콘솔
    • 홈화면과 콘솔 모두 전과 동일하다.
    • 홈화면에는 동영상이 멈춘 상태로 로딩이 끝났고 콘솔에서 확인하길 파일은 쓰다 말았다.
  6. 그래서 동영상 재생 버튼을 눌러보았다.

    • 콘솔

      파일을 처음부터 끝까지 다 쓴다.
  7. 지금까지 알게된 것

    이 실험을 통해 두가지 사실을 알게 되었다.

    1. 처음 동영상이 로딩될 때 서버측에서 write를 해줄 때 시그널 인터럽트를 당하는 것 같다. 그래서 쓰다가 만다.
    2. 동영상 로딩과, 동영상 재생이 각각 두번의 요청을 보내는 것으로 판단된다.
    3. 그래서 처음 코드상에선 동영상이 자동으로 재생되는 주소 host:8000/happy.mp4 는 서버가 꺼져버렸고, 자동으로 재생이 안되는 홈화면에선 서버가 안꺼지고 있었다고 추측된다.
    4. 뭔가 두개의 요청이 충돌을 일으키는 느낌이다.
  8. 그래서 if를 다시 while로 바꾸고 대신 nleft가 남았을 때 sleep을 좀 하다가 다시 반복문에 들어가도록 코드를 바꾸어보았다.

    • 코드
    ssize_t rio_writen(int fd, void *usrbuf, size_t n)
    {
        size_t nleft = n;
        ssize_t nwritten;
        char *bufp = usrbuf;
        printf("filesize: %d\n", n);
        while (nleft > 0)
        {
            if ((nwritten = write(fd, bufp, nleft)) <= 0)
            {
                if (errno == EINTR) /* Interrupted by sig handler return */
                    nwritten = 0;   /* and call write() again */
                else
                {
                    printf("end2\n");
                    return -1; /* errno set by write() */
                }
            }
            nleft -= nwritten;
            printf("leftsize: %d\n", nleft);
            bufp += nwritten;
            if (nleft)
                usleep(50);
        }
        printf("end1\n");
        return n;
    }
    • 이렇게 했더니 굉장히 잘된다.
    • 여전히 홈 화면에선 처음에 파일을 쓰다 말고, 재생을 눌리면 다시 처음부터 끝까지 전부 쓴다.
    • 이상한 점은 홈 화면이 떴을 때 바로 재생 버튼을 누르면 쓰다 말지 않고 바로 끝까지 다 써버린다는 것이다. 어디선가 인터럽트 하던 동작이 사라지는 것처럼 보인다.
  9. 내가 생각하는 정상적인 동작

    • 내가 파악한 바에 따르면 브라우저에서 보내는 모든 요청은 서버 측에서 각각의 요청으로 받아들여져서 연결 대기 큐에 들어있다가 순차적으로 수행된다.
    • 동영상 로딩과 재생이 각각 다른 요청이라고 치면 ( 홈화면에서 보여주었던 동작을 바탕으로 추측한 사항 - 재생버튼을 누르면 요청이 하나 더 보내졌던.. ) 일단 동영상의 일부분만 페이지 띄우는 요청과, 다시 동영상을 재생시키는 요청이 둘다 콘솔에 떠야한다.
    • 그러나 usleep(50)을 추가하고 나니, 홈화면을 로딩하고 다시 재생을 눌렀을 때 요청이 하나밖에 안들어온다. (usleep을 추가하기 전엔 그렇게 하면 서버가 꺼졌다..)
  10. 원인에 대한 나의 추측

    • 하나의 파일 디스크럽터에 서버와 클라이언트가 동시에 뭔가 작성하기 시작해서 (동영상 파일과 요청헤더(재생하면 한번 더 보내지는)) 오류가 발생하는 것 같다.
    • 시스템 콜에 대해 이해가 부족해 좀 더 공부하고 생각해봐야할 것 같다..
profile
새싹 -> 나무

0개의 댓글