태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

eternally full with hope...

블로그 이미지
영원히 희망으로 가득하길...
by eternalbleu
  • 464617Total hit
  • 100Today hit
  • 316Yesterday hit

'프로그래밍'에 해당되는 글 7건

  1. 2008/07/03
    Proggy Programming Fonts
  2. 2006/07/05
    프로그래밍에서 가장 힘든것은...
  3. 2006/05/31
    작업의 끝
  4. 2006/04/13
    웹서버 소스
  5. 2006/04/05
    웹 프락시 작성
  6. 2006/03/27
    다중 연결 허용 서버의 구현법 (socket programming)
  7. 2006/02/01
    흠... -_-;;
IDE를 켜고 영어를 적다보면 생기는 비슷한 글자를 최대한 줄여주는 폰트가 바로 좋은 폰트 ㅎㅎㅎ

사용자 삽입 이미지

http://www.proggyfonts.com/

연산자나 브래킷을 강조해주는 폰트가 가장 편한듯함.

사용자 삽입 이미지

TRACKBACK 0 AND COMMENT 0
바로 프로그래밍을 시작하는 것이다.

프로그래밍을 하면서 가장 힘든건 바로 이것!!!!

오늘 준비중인 일의 마무리를 하려고 학교에 왔다. 그동안 왔다갔다 했지만 학교에서 놀기만 하다가 갔는데... -_-;; 결국 오늘은 코딩을 하고야 말았다. ㅎㅎㅎ 어쨋든 시작하니깐 이게 왠일;; 1시간도 안되서 끝낸것 같다;;;

제길;; 도대체 이게 먼 뻘짓이지... 맨날;;

결국 시작하는데에만 근 일주일 가까이 걸린거 같다. 하여간 웃긴 인생... 이제 발표 준비랑 해야겠다. 흠 흠 ㅎㅎㅎ
TRACKBACK 0 AND COMMENT 0

-_- 기대하신 작업이 아니리라 생각합니다. 므흣~

오늘 드디어 3주간 밤을 새면서 작업한 개인 프로젝트의 대략 90% 이상을 달성하였습니다. 시험이 겹쳐서 시간이 굉장히 촉박했기 때문에 좀 무리한 감이 있지않을까? 그런 생각이 들 정도로 정말로 열심히 프로그램을 짠 것 같습니다.

혼자서 스스로 짜는 프로그램으로 이렇게 3주일을 밤을 새면서 작성한 경우는 이번에 처음인 것 같네요. 결과가 좋아야할 텐데... 어떨지 모르겠습니다. 노력한 만큼의 결과가 항상 나오는 것이 아니니까 너무 불안하네요.

어쨋든 이제 남은 것은 지원서와 관련 지식의 공부. 그리고 디버깅과 남은 10%의 모듈을 완성하는 것입니다. 이제 완성도를 높이고 자잘한 버그들을 잡아야할 시기인 것이죠. 일단은 학과 공부 모드로 스위치합니다. 오늘 하루도 열심히 사는 하루가되길~

TRACKBACK 0 AND COMMENT 0

/************************************************************************
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), &amp;wsaData) != 0 )
ErrorHandling("WSAStartup() error occured.");

// socket creation &amp; initialize
if ( (servSock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET )
ErrorHandling("socket() error occured.");

// 서버 소켓 설정
memset((void*)&amp;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*)&amp;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*)&amp;clntAddr, &amp;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*)&amp;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 &amp;&amp; !strcmp(filename, "/") ) strcpy(filename, "/index.html");
if ( *filename == '/' ) filename++;

// file open
if ( statusType == STATECODE_OK &amp;&amp; (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 &lt; 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);
}


윈도우 환경에서 동작하는 서버입니다. 약간의 수정을 하면 리눅스 환경에서도 사용 가능함.
TRACKBACK 0 AND COMMENT 0
HTTP 프락시 작성기

HTTP를 이용한 프락시 서버의 목적은 기본적으로 공용 캐쉬이다. 여기에 그룹 보안 및 미들웨어 수준의 통합이 이루어지면 이 것이 바로 상용 프락시가 되는 것이다.

이번에 작성한 프락시는 가장 단순한 형태로 구현하기로 목표를 잡았다.


프로그램에 대한 접근을 한 방식은 다음과 같다.

1. 브라우저와 소캣을 통해 브라우저의 Proxy Server 로의 요청 메시지를 분석한다.

2. Proxy Server 와 웹 서버 단의 메시지를 분석한다.

3. 분석된 서로간의 메시지를 테스트 하면서 동작이 어떤 식으로 연계되는지 확인하고 간단한 HTTP replay 서버를 실 코드로 작성한다. (멀티 쓰레드 기반)

4. 웹 서버에서 브라우저로 보내는 응답 메시지를 Proxy Server 에서 수신, 파일로 저장하는 실 코드를 작성한다.

5. 저장된 파일을 관리할 수 있는 인덱스 파일의 스키마를 생각한다. 스키마는 최대한 단순해야하며, 가능하면 plain text 를 이용. 차후 프로그램의 테스트에 단순 에디터의 사용만으로 테스팅이 가능하도록 함.

6. 구성된 스키마를 바탕으로 validate 를 결정할 수 있는 프로시져, 모듈을 작성한다. (Validate 라함은 실제 캐쉬의 동작방식을 함께 포함하는 개념이다. 따라서 이 정도로 프로젝트를 진행했다면 캐쉬 방법에 대해서 한번쯤 알아보고 자신에게 적합한 방식을 선택한다.)

7. 완성된 모듈을 현재까지 작업한 Proxy Server 와 통합한다.


상기 과정을 통해서 전체적인 프락시 서버를 완성하는 것이 가능하다.

프로젝트의 난이도는 중간정도.
TRACKBACK 0 AND COMMENT 0

다중 사용자의 connect 를 처리하기 위한 방법은 총 3가지의 방식으로 구분한다.

1. 유닉스 호환환경에서 제공하는 fork() 를 이용한 다중 프로세스
2. IO 멀티플렉싱을 이용한 다중 연결
3. 멀티 쓰레드를 이용한 다중 연결

3가지의 방법은 어떤 것이 좋다 나쁘다고 할 것이 아니고 적당한 상황하에서 적정한 기술을 이용하는 것이 관건이라고 할 수 있을 것이다.

1. fork() 이용 소스
[CODE] /** 멀티 프로세스를 이용한 에코 서버 프로그램 리눅스 환경하에서 컴파일 하세요. 출처 : 열혈강의 TCP/IP **/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/socket.h> #define BUFFSIZE 30 void error_handling(const char* message); void z_handler(int sig); int main(int argc, char **argv) { int serv_sock; int clnt_sock; struct sockaddr_in serv_addr; struct sockaddr_in clnt_addr; struct sigaction act; int addr_size, str_len, state; pid_t pid; char message[BUFFSIZE]; if(argc!=2) { printf("USAGE: %s <port>\n", argv[0]); exit(1); } act.sa_handler = z_handler; sigemptyset(&act.sa_mask); act.sa_flags=0; state = sigaction(SIGCHLD, &act, 0); if (state != 0) { puts("sigaction() error!"); exit(1); } serv_sock = socket(PF_INET, SOCK_STREAM, 0); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(atoi(argv[1])); if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) error_handling("bind() error"); if(listen(serv_sock, 5) == -1) error_handling("listen() error"); while(1) { addr_size = sizeof(clnt_addr); clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &addr_size); if (clnt_sock == -1) continue; if ((pid ==fork()) == -1) {   close(clnt_sock);   continue; } else if (pid >0) {   puts("connection success");   close("clnt_sock");   continue; } else {   close(serv_sock);   while( (str_len = read(clnt_sock, message, BUFFSIZE)) != 0) {   write(clnt_sock, message, str_len);   write(1, message, str_len);   }   puts("connection closing");   close(clnt_sock);   exit(0); } } return 0; } void z_handler(int sig) { pid_t pid; int rtn; pid = waitpid(-1, &rtn, WNOHANG); printf("zombi process ID: %d\n", pid); printf("return data : %d\n", WEXITSTATUS(rtn)); } void error_handling(const char* message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } [/CODE]

2. select 이용 소스
[CODE] /** IO 멀티플렉싱을 이용한 에코 서버 프로그램 리눅스 환경하에서 컴파일 하세요. 출처 : 열혈강의 TCP/IP **/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/time.h> #include <netinet/in.h> #define BUFFSIZE 100 void error_handling(const char* message); int main(int argc, char **argv) { int serv_sock; struct sockaddr_in serv_addr; fd_set reads, temps; int fd_max; char message[BUFFSIZE]; int str_len; struct timeval timeout; if (argc != 2) { printf("USAGE : %s <port>\n", argv[0]); exit(1); } serv_sock = socket(PF_INET, SOCK_STREAM, 0); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(atoi(argv[1])); if (bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) error_handling("bind() error"); if (listen(serv_sock, 5) == -1) error_handling("listen() error"); FD_ZERO(&reads); FD_SET(serv_sock, &reads); fd_max = serv_sock; while (1) { int fd, str_len; int clnt_sock, clnt_len; struct sockaddr_in clnt_addr; temps = reads; timeout.tv_sec = 5; timeout.tv_usec = 0; if (select(fd_max + 1, &temps, 0, 0, &timeout) == -1)   error_handling("select() error"); for (fd = 0; fd < fd_max+1; fd++) {   if(fd == serv_sock) {   clnt_len = sizeof(clnt_addr);   clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_len);   FD_SET(clnt_sock, &reads);   if (fd_max < clnt_sock)   fd_max = clnt_sock;   printf("client connection : file descriptor %d\n", clnt_sock);   }   else {   str_len = read(fd, message, BUFFSIZE);   if (str_len == 0) {   FD_CLR(fd, &reads);   close(fd);   printf("client closing: file descriptor %d\n", fd);     }   else {   write(fd, message, str_len);   }   } } } } void error_handling(const char* message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } [/CODE]
TRACKBACK 0 AND COMMENT 0
모르겠다 어디냐 ㅡㅡ;; 버그지점;;;
TRACKBACK 0 AND COMMENT 0

ARTICLE CATEGORY

분류 전체보기 (782)
Re: myself (441)
Favorite things (241)
CS&E (71)
Portfolio (22)
삼성SDS (0)

CALENDAR

«   2008/07   »
    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    

ARCHIVE