본문 바로가기

개발/서버

네트워킹 - 작은 웹서버 , 웹서버 원리 제작

TCP/IP 연결을 받아들인 후 웹서버는 HTTP 프로토콜을 사용해 다음 계층을 구현할 필요가 있다.
다음의 서버 코드는 연결 제어 코드가 함수에서 분리됐다는 점만 빼고 앞에서 살펴본 간단한 서버예제와
거의 같다.  이 함수는 웹 브라우저에서 온 HTTP GET과 HEAD 요청을 다룬다. 요청 받은 리소스를
웹루트라는 로컬 디렉터리에서 찾아 브라우저로 보낸다. 파일을 찾지 못하면 서버는 404 HTTP를 응답한다.
File Not Found 응답은 자주 봤을 것이다.

---------------------------- 작은 웹서버 코드 --------------------------------
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "hacking.h"
#include "hacking-network.h"
#define PORT 80
#define WEBROOT "./webroot"
void handle_connection(int, struct sockaddr_in *);
int get_file_size(int fd);
void Error(const char *mes);
int main()
{
        int sockfd, new_sockfd, yes = 1;
        struct sockaddr_in host_addr, client_addr;
        socklen_t sin_size;
        printf("Web request accepting... [ Port : %d ]\n",PORT);
        if( (sockfd = socket(PF_INET,SOCK_STREAM,0)) == -1)
                Error("socket Error\n");

        if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)) == -1)
                Error("setsockopt Error\n");

        memset(&host_addr,0,sizeof(host_addr));
        host_addr.sin_family = AF_INET;
        host_addr.sin_addr.s_addr = INADDR_ANY;
        host_addr.sin_port = htons(PORT);
        if(bind(sockfd,(struct sockaddr*)&host_addr,sizeof(host_addr)) == -1)
                Error("bind Error\n");
        if( listen(sockfd, 20) == -1)
                Error("listen Error\n");
        while(1)
        {
                sin_size = sizeof(host_addr);
                new_sockfd = accept(sockfd,(struct sockaddr*)&client_addr,&sin_size);
                if(new_sockfd == -1)
                        Error("accept Error\n");
                handle_connection(new_sockfd,&client_addr);
        }
        return 0;
}
void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr)
{
        unsigned char *ptr, request[500], resource[500];
        int fd, length;
        length = recv_line(sockfd,request);
        printf("request [%s:%d] \"%s\"\n",inet_ntoa(client_addr_ptr->sin_addr),ntohs(client_addr_ptr-
>sin_port),request);
        ptr = strstr(request," HTTP/");
        if(ptr == NULL)
        {
                printf(" NOT HTTP!\n");
        }
        else
        {
                *ptr = 0;
                ptr = NULL;
                if(strncmp(request,"GET ",4) == 0)
                        ptr = request + 4;
                if(strncmp(request,"HEAD ",5) == 0)
                        ptr = request+5;
                if(ptr == NULL)
                        printf("\tUnKnown REQUEST!\n");
                else
                {
                        if(ptr[strlen(ptr)-1] == '/')
                                strcat(ptr,"index.html");
                        strcpy(resource,WEBROOT);
                        strcat(resource,ptr);
                        fd = open(resource,O_RDONLY,0);
                        printf("\t FileOpen \'%s\'\t",resource);
                        if(fd == -1)
                        {
                                send_string(sockfd,"HTTP/1.0 404 NOT FOUND\r\n");
                                send_string(sockfd,"<html><head><title>404 Not Found</title></head>");
                                send_string(sockfd,"<body><h1>URL not found</h1></body></html>\r\n");
                        }
                        else
                        {
                                printf("  200 OK\n");
                                send_string(sockfd,"HTTP/1.0 200 OK\r\n");
                                send_string(sockfd,"Server: Tiny webserver\r\n\r\n");
                                if(ptr == request + 4)
                                {
                                        if( (length = get_file_size(fd)) == -1)
                                                Error("get_file_size Error\n");
                                        if( (ptr = (unsigned char*)malloc(length)) == NULL)
                                                Error("malloc Error\n");
                                        read(fd,ptr,length);
                                        send(sockfd,ptr,length,0);
                                        free(ptr);
                                }
                        }
                        close(fd);

                }
        }
        shutdown(sockfd,SHUT_RDWR);
}
int get_file_size(int fd)
{
        struct stat stat_struct;
        if(fstat(fd,&stat_struct) == -1)
                return -1;
        return (int) stat_struct.st_size;
}

void Error(const char *mes)
{
        printf("%s\n",mes);
        exit(0);
}
-----------------------------------------------------------------------------
위의 코드가 아파치 와 같은 웹 서버를 흉내낸 것이다.

이 프로그램이 실해되는 경로에 webroot 라는 폴더를 만들고 그곳에다가 index.html 파일과
test.jpg 라는 파일을 만들어보자. (jpg 파일은 아무 그림파일을 복사해 넣자)
그리고 위의 프로그램을 실행시키면 80번 포트를 열고 웹 브라우저의 접속을 기다린다.
(포트가 열리지 않는다면 기존의 80번 포트를 사용하고 있는 아파치와 같은 웹서버를 잠시 중시키고 다시한다.)
그리고 외부에서 웹 브라우저를 통해  위의 서버를 실행한 곳의 아이피주소를 적고 접속하면
서버의 /webroot/index.html 파일이 보여지게 된다.

-----------------------------------------------------------------------------
[ 서버 결과화면 ]
 
[root@localhost http]# ./server.exe
Web request accepting... [ Port : 80 ]
request [192.168.91.1:7329] "GET / HTTP/1.1"
         FileOpen './webroot/index.html'          200 OK
request [192.168.91.1:7334] "GET /test.jpg HTTP/1.1"
         FileOpen './webroot/test.jpg'    200 OK
 
 
-----------------------------------------------------------------------------

[ 클라이언트 결과화면 ]
 
http://서버아이피/
http://서버아이피/test.jpg
위와 같이 웹브라우저에서 접속한다면
index.html 문서와 test.jpg 그림파일이 보일것이다.
-----------------------------------------------------------------------------
handle_connection 함수는 요청 받은 버퍼의 HTTP/ 문자열을 찾는 데 strstr() 함수를 사용한다.
strstr() 함수는 요청에서 HTTP/ 문자열이 처음 발견된 위치의 포인터를 리턴한다. 그 후에는
리턴받은 포인터를 널로 만들고 HEAD와 GET 요청을 계속 진행해도 되는지 확인한다. HEAD 요청에 대해서는
헤더만 리턴하고, GET 요청에 대해서는 (리소스가 있다면) 요청한 리소스를 리턴한다.

'개발 > 서버' 카테고리의 다른 글

nginx 구라에 속지말자  (0) 2013.06.14
프로그래밍 - gdb , objdump  (0) 2012.02.05
[ gdb 로 스택 구조 확인 ]  (0) 2012.02.05
프로세스  (0) 2012.02.05
사용자 계정 관리  (0) 2012.02.05