티스토리 뷰
/************************************************************************
Multithreaded Web Server
written by eternalbleu
************************************************************************/
#include <STDIO.H>
#include <STDLIB.H>
#include <STRING.H>
#include <WINSOCK2.H>
#include <PROCESS.H>
// Constant
#define HUG_BUFFSIZE 2048
#define MID_BUFFSIZE 1024
#define SMA_BUFFSIZE 32
#define MAX_FILENAME 256
// Error Code
#define STATECODE_BAD_REQUEST 400
#define STATECODE_NOT_FOUND 404
#define STATECODE_OK 200
// Send HTTP Replay to Client
void SendReply(SOCKET clnt, UINT statusType, char *filename);
char* GetStatusLine(UINT type = STATECODE_OK);
char* GetErrorContent(UINT type);
int GetContentLength(FILE* fp);
char* GetContentType(char* filename);
void ErrorHandling(const char* message); // Error Message Handling
UINT WINAPI ClientConnect(void* argv); // Thread Proc
int main(int argc, char** argv)
{
WSADATA wsaData;
SOCKET servSock;
SOCKET clntSock;
SOCKADDR_IN servAddr;
SOCKADDR_IN clntAddr;
HANDLE hThread;
DWORD dwThreadID;
if (argc != 2) {
printf("SIMPLE WEBSERVER. written by youngchang. \n USAGE: %s <PORT>\n", argv[0]);
exit(1);
}
// DLL loading
if ( WSAStartup(MAKEWORD(2, 2), &wsaData) != 0 )
ErrorHandling("WSAStartup() error occured.");
// socket creation & initialize
if ( (servSock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET )
ErrorHandling("socket() error occured.");
// 서버 소켓 설정
memset((void*)&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET; // IPv4 주소 체계 이용
servAddr.sin_addr.s_addr = htonl( INADDR_ANY ); // 서버 주소 할당. 임의의 주소가 될 수 있어야함.
servAddr.sin_port = htons( atoi(argv[1]) ); // 인자로 받은 숫자를 이용 포트를 할당
// server socket setting
if ( bind(servSock, (SOCKADDR*)&servAddr, sizeof(servAddr) ) == SOCKET_ERROR )
ErrorHandling("bind() error occured.");
// wait for request from client
if ( listen(servSock, 5) == SOCKET_ERROR )
ErrorHandling("listen() error occured.");
// requested from client
while(1) {
// 서버 대기 소켓으로 연결 요청이 들어온 클라이언트와의 소켓을 생성
int clntAddrSize = sizeof(clntAddr);
if ( (clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &clntAddrSize)) == INVALID_SOCKET )
ErrorHandling("accept() error occured.");
// 클라이언트의 정보 출력
#ifdef _DEBUG
printf("REQUEST FROM %s : %d\n", inet_ntoa(clntAddr.sin_addr), ntohs(clntAddr.sin_port));
#endif
// 클라이언트 연결 쓰레드 생성
hThread = (HANDLE)_beginthreadex(NULL, 0, ClientConnect, (void*)clntSock, 0, (unsigned*)&dwThreadID);
if (hThread == NULL)
ErrorHandling("_beginthreadex() error occured.");
}
// socket destroy
closesocket(servSock);
// DLL Unloading
WSACleanup();
return 0;
}
/************************************************************************
ClientConnect 합당한 에러메시지를 출력하며 프로그램을 종료한다.
@ argv 쓰레드프로세저의 인자. 이 경우 클라이언트와 연결된 소켓이 인자임.
# return 에러발생시 1, 정상 종료시 0
************************************************************************/
UINT WINAPI ClientConnect(void* argv)
{
SOCKET clntSock = (SOCKET)argv; // 클라이언트 소켓
char request[HUG_BUFFSIZE] = {0,}; // 요청 라인 (string)
char filename[MAX_FILENAME] = {0,}; // 요청 파일 (string)
char method[SMA_BUFFSIZE] = {0,}; // 요청 방식 (METHOD)
recv(clntSock, request, sizeof(request), 0);
if ( strstr(request, "HTTP/") == NULL ) // HTTP 요청인지 검사
{
closesocket(clntSock);
return 1;
}
// HTTP 요청 정보 출력
#ifdef _DEBUG
fputs("[BEG]\n", stdout);
fputs(request, stdout);
fputs("\n[END]\n", stdout);
#endif
strcpy(method, strtok(request, " ")); // HTTP 요청 방식 (METHOD) 얻기
if (strcmp(method, "GET")) // POST 메소드인 경우 에러 처리
SendReply(clntSock, STATECODE_BAD_REQUEST, NULL);
strcpy(filename, strtok(NULL, " ")); // HTTP 요청 파일 얻기
SendReply(clntSock, STATECODE_OK, filename);
return 0;
}
/************************************************************************
SendReply 클라이언트에게 알맞은 정보를 송신한다.
@ clnt 연결된 클라이언트와의 통신 소켓
@ statusType 답신 메시지의 종류를 송신한다
@ filename 클라이언트에게 보낼 파일 이름
************************************************************************/
void SendReply(SOCKET clnt, UINT statusType, char *filename)
{
// HEADER
char statusLine[SMA_BUFFSIZE] = {0,}; // 상태 라인
char contentLength[SMA_BUFFSIZE]= {0,}; // 내용 길이
char contentType[SMA_BUFFSIZE] = {0,}; // 내용 종류
char serverName[] = "Server:Simple Web Server v1.0\r\n"; // 서버 정보
// DATA
FILE* fp; // 전송 파일 파온터
char buffer[HUG_BUFFSIZE] = {0,}; // 전송 버퍼
// pre-process filename
if ( strlen(filename) == 1 && !strcmp(filename, "/") ) strcpy(filename, "/index.html");
if ( *filename == '/' ) filename++;
// file open
if ( statusType == STATECODE_OK && (fp = fopen(filename, "rb")) == NULL ) {
statusType = STATECODE_NOT_FOUND;
}
strcpy(statusLine, GetStatusLine(statusType)); // 상태 라인 정보 얻기
strcpy(contentType, GetContentType(filename)); // 내용 종류 얻기
sprintf(contentLength, "Content-length:%d\r\n", GetContentLength(fp)); // 내용 길이 정보 얻기
// Send HTTP Header
send(clnt, statusLine, strlen(statusLine), 0);
send(clnt, serverName, strlen(serverName), 0);
send(clnt, contentLength, strlen(contentLength), 0);
send(clnt, contentType, strlen(contentType), 0);
// Transmit error page data
if (statusType != STATECODE_OK)
{
char tmpbuf[MID_BUFFSIZE] = {0,};
strcpy(tmpbuf, GetErrorContent(statusType));
send(clnt, tmpbuf, strlen(tmpbuf), 0);
closesocket(clnt);
return;
}
// Send HTTP Data
size_t length = 0;
do {
length = fread(buffer, sizeof(char), sizeof(buffer), fp);
send (clnt, buffer, length, 0);
} while(!feof(fp));
closesocket(clnt);
}
/************************************************************************
GetStatusLine
@ type 상태 라인을 얻기위한 상수 값
# return 상태 라인 정보
************************************************************************/
char* GetStatusLine(UINT type)
{
switch(type)
{
case STATECODE_OK:
return "HTTP/1.0 200 OK\r\n";
break;
case STATECODE_BAD_REQUEST:
return "HTTP/1.0 400 Bad Request\r\n";
break;
case STATECODE_NOT_FOUND:
return "HTTP/1.0 404 Not Found\r\n";
break;
default:
return "HTTP/1.0 400 Bad Request\r\n";
break;
}
}
/************************************************************************
GetErrorContent 에러 코드에 따른 페이지 전송
@ type 에러 코드 상수
# return 에러코드에 맞는 페이지의 스트링
************************************************************************/
char* GetErrorContent(UINT type)
{
char buffer[HUG_BUFFSIZE] = {0,};
switch(type)
{
case STATECODE_NOT_FOUND:
strcpy(buffer,
"<FONT size=5>404 NOT FOUND</FONT>
ERROR OCCURED. CHECK FILENAME."
);
break;
case STATECODE_BAD_REQUEST:
strcpy(buffer,
"<FONT size=5>400 BAD REQUEST</FONT>
ERROR OCCURED. CHECK REQ METHOD."
);
break;
default:
strcpy(buffer,
"<FONT size=5>400 BAD REQUEST</FONT>
ERROR OCCURED. CHECK REQ METHOD."
);
break;
}
return buffer;
}
/************************************************************************
GetContentType 파일의 이름을 기반으로 파일의 MIME 타입의 스트링을 리턴한다.
@ filename 타입을 알아야할 파일의 이름
# return 파일의 MIME 타입 스트링
************************************************************************/
char* GetContentType(char* filename)
{
char retvalue[SMA_BUFFSIZE];
for(UINT i = 0; i < strlen(filename); i++)
*(filename+i) = tolower( *(filename+i) );
if ( strstr(filename, ".html") != NULL || strstr(filename, ".htm") != NULL )
sprintf(retvalue, "Content-type:%s\r\n\r\n", "text/html");
if ( strstr(filename, ".txt") != NULL )
sprintf(retvalue, "Content-type:%s\r\n\r\n", "text/plain");
if ( strstr(filename, ".jpeg") != NULL || strstr(filename, ".jpg") != NULL )
sprintf(retvalue, "Content-type:%s\r\n\r\n", "image/jpeg");
if ( strstr(filename, ".png") != NULL )
sprintf(retvalue, "Content-type:%s\r\n\r\n", "image/pjpeg");
if ( strstr(filename, ".gif") != NULL )
sprintf(retvalue, "Content-type:%s\r\n\r\n", "image/gif");
if ( strstr(filename, ".bmp") != NULL )
sprintf(retvalue, "Content-type:%s\r\n\r\n", "image/x-xbitmap");
return retvalue;
}
/************************************************************************
GetContentLength 요청 받은 파일의 크기를 바이트 단위로 리턴한다. (in byte)
@ fp 요청 받은 파일의 포인터
# return file size (in byte)
************************************************************************/
int GetContentLength(FILE* fp)
{
if (fp == NULL) return 0;
unsigned int length = 0;
char buf[HUG_BUFFSIZE];
while ( !feof(fp) )
{
length += fread(buf, sizeof(char), sizeof(buf), fp);
}
fseek(fp, 0, SEEK_SET); //rewind(fp);
return length;
}
/************************************************************************
ErrorHandling 합당한 에러메시지를 출력하며 프로그램을 종료한다.
@ message 프로그램 에러 메시지
************************************************************************/
void ErrorHandling(const char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
윈도우 환경에서 동작하는 서버입니다. 약간의 수정을 하면 리눅스 환경에서도 사용 가능함.
- Total
- Today
- Yesterday
- wow
- 책
- oracle
- 시간표
- 실전! 업무에 바로 쓰는 SQL 튜닝
- 박영창
- hp-ux
- 과제물
- 오라클
- 회식
- World Of Warcraft
- HPUX
- SQL 튜닝
- 프로그래밍
- 삼성 소프트웨어 멤버십
- SSM
- 오픈 소스 SW와 전략적 활용
- Japanimation
- 네트워크
- 와우
- 애니메이션
- 실습으로 배우는 Unix System Admin (HPUX)
- 후기
- 모임
- 리눅스
- 영화
- 레포트
- 일기
- 캐논
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |