네트워킹 - 작은 웹서버 , 웹서버 원리 제작
2012. 2. 5. 22:46ㆍ개발/서버
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 |