Chinaunix首页 | 论坛 | 博客
  • 博客访问: 181786
  • 博文数量: 11
  • 博客积分: 478
  • 博客等级: 下士
  • 技术积分: 264
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-20 11:45
文章分类
文章存档

2011年(1)

2009年(10)

我的朋友

分类: 系统运维

2009-10-28 22:00:00

上一页
webserver.c


/* file: webserver.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h>
#include "config.h"
#include "init_socket.h"
#include "http_session.h"
#include "sig.h"




int main(int argc, char *argv[])
{
    
    /* read configure file from web server config path */
    if(config() == -1)
    {
#ifdef DEBUG
        printf("Read webconfig.conf, use the default values [error]\n");
#endif
    }
    
#ifdef DEBUG
    printf("Read webconfig.conf [ok]\n");
#endif
    
    /* */
    extern char WEB_ROOT_PATH[];
#ifdef DEBUG
    printf("WEB_ROOT_PATH in main: %s\n", WEB_ROOT_PATH);
#endif
    if(chdir(WEB_ROOT_PATH) == -1)
    {
        perror("chdir error");    
        return -1;
    }
    
    
    
    /*
    * register a signal handler to deal with zombie child process.
    */

    struct sigaction act;
    act.sa_handler = sig_function;
    sigemptyset(&act.sa_mask);    /* not block any signals */
    act.sa_flags = 0;
    
#ifdef    SA_INTERRUPT
        act.sa_flags |= SA_INTERRUPT;    /* not declare in linux kernel 2.6 */
#endif

#ifdef SA_RESTART
        act.sa_flags |= SA_RESTART;        /* auto restart the interrupted system call */
#endif

    if(sigaction(SIGCHLD, &act, NULL) == -1)     /* register SIGCHLD to sig_function */
    {
#ifdef DEBUG
        printf("sigaction error in webserver.c [error]\n");
#endif
        exit(EXIT_FAILURE);
    }
    /* end of signal handler register */
    
#ifdef DEBUG
    printf("Register SIG_CHLD function [ok]\n");
#endif


    /*********************************************************
    * create the server , and prepare to accept client connect
    **********************************************************/

    int listen_fd;
    int connect_fd;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    bzero(&server_addr, sizeof(struct sockaddr_in));
    bzero(&server_addr, sizeof(struct sockaddr_in));

    if(init_socket(&listen_fd, &server_addr) == -1)
    {
#ifdef DEBUG
        printf("init_socket() error. in webserver.c [error]\n");
#endif
        exit(EXIT_FAILURE);
    }

#ifdef DEBUG
    printf("initilize server socket [ok]\n");
#endif
    
    socklen_t addrlen = sizeof(struct sockaddr_in);
    pid_t pid;
    
    int fork_error_count = 10;
#ifdef DEBUG
    printf("Ready to accept conncetion...\n");
#endif
    while(1)
    {
        if((connect_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addrlen)) == -1)
        {
            if(errno == EINTR)        /* restart the interrupted system call */
            {
                continue;
            }
            else
            {
#ifdef DEBUG
                printf("accept() error. in webserver.c [error]");
#endif
                exit(EXIT_FAILURE);
            }
        }
        if( (pid = fork()) > 0)        /* parent process */
        {
            close(connect_fd);
            fork_error_count = 10;
            continue;
        }
        else if(pid == 0)            /* child process */
        {
            close(listen_fd);
#ifdef DEBUG
            printf("pid %d process http session from %s : %d\n", getpid(), inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port));
                
            printf("start a http session\n");
#endif
            if(http_session(connect_fd) == -1)
            {
                /* some error occure */
#ifdef DEBUG
                printf("http_session() error. in webserver.c");
#endif
                shutdown(connect_fd, SHUT_RDWR);    /* close the socket */
#ifdef DEBUG
                printf("pid %d loss connection to %s\n", getpid(), inet_ntoa(client_addr.sin_addr));
#endif
                exit(EXIT_FAILURE);        /* exit from child process, stop this http session */
            }
        
            /* normal exit */
            else
            {
#ifdef DEBUG
                printf("pid %d close connection to %s\n", getpid(), inet_ntoa(client_addr.sin_addr));
#endif
                shutdown(connect_fd, SHUT_RDWR);
                exit(EXIT_SUCCESS);
            }
        }
        
        /* server fork error, some resource may not avaiable ,so sleep 2 secends ,and try again */
        else
        {
            fork_error_count--;
            if(fork_error_count > 0)    /* after fork 10 times if all error , then shut down the server */
            {
                sleep(2);    /* fork error, may no resource avaiable, so sleep 2 secend */
                continue;
            }
            else
            {
                exit(EXIT_FAILURE);
            }
        }
    
    }


    shutdown(listen_fd, SHUT_RDWR);
    return 0;
}



sig.h

/* file: sig.h */
#ifndef SIG_H
#define SIG_H

void sig_function(int signo);

#endif



sig.c

/* file: sig.c */
#include <sys/wait.h>
#include <signal.h>
#include "sig.h"

void sig_function(int signo)
{
    pid_t pid;
    int status;
    if(signo == SIGCHLD)
    {
        while( (pid = waitpid(-1, &status, WNOHANG)) > 0)    /* wait all zombie childs proccess */
            ;
    }
    
    return;
}


init_socket.h

/* file: init_socket.h */
#ifndef INIT_SOCKET_H
#define INIT_SOCKET_H
#include <netinet/in.h>



/* initialize the socket on server, include below
   socket();
   bind();
   listen();
*/



/* listen_fd : the web server listen file decriptor
   server_addr: the web server ipv4 address
   RETURNS: success on 0, error on -1
*/

int init_socket(int *listen_fd, struct sockaddr_in *server_addr);


#endif


init_socket.c

/* file: init_socket.c */
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include "init_socket.h"


int init_socket(int *listen_fd, struct sockaddr_in *server_addr)
{
    extern u_int16_t    PORT;
    extern int BACKLOG;
    if((*listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)    
    {
        perror("socket() error. in init_socket.c");
        return -1;
    }

    /* set reuse the port on server machine */
    int opt = SO_REUSEADDR;
    if(setsockopt(*listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
    {
        perror("setsockopt() error. in init_socket.c");
        return -1;
    }
    
    
    
    server_addr->sin_family = AF_INET;
    server_addr->sin_port = htons(PORT);
    server_addr->sin_addr.s_addr = htonl(INADDR_ANY);

    if(bind(*listen_fd, (struct sockaddr *)server_addr, sizeof(struct sockaddr_in)) == -1)
    {
        if(errno == EACCES)
        {
            printf("Permission deny, You must be ROOT to bind this port.\nIf you are not, please modify the webconfig.conf ,change the port to a new number greater than 1024\n");
            return -1;
        }
        perror("bind() error. in init_socket.c");
        return -1;
    }

    if(listen(*listen_fd, BACKLOG) == -1)
    {
        perror("listen() error. in init_socket.c");
        return -1;
    }
    
    return 0;
}



config.h

/* file: config.h */
#ifndef CONFIG_H
#define CONFIG_H

/* the webconfig.conf file each line's max length */
#define CONFIG_LINE_MAX_LENTH 128


extern short PORT ;    /*may 16 bits */

extern int BACKLOG ;

extern size_t RECV_BUFFER_SIZE ;

extern size_t SEND_BUFFER_SIZE ;

extern size_t TIME_BUFFER_SIZE ;

extern size_t URI_SIZE ;

extern size_t MIME_BUFFER_SIZE ;

extern size_t FILE_MAX_SIZE ;

/* read the web server config file -- 'webconfig.conf' ,and set it into the server */
int config(void);

#endif


config.c

/* file: config.c */
#include <stdio.h>
#include <string.h>
#include "config.h"

/* the follow global values are default web server configs */
#define CONFIG_PATH            "../config/webconfig.conf"
#define WEB_ROOT_PATH_LEN    30            /* web server root directory is length , default */
#define    WEB_CACH_PATH_LEN    30            /* web server cache directory is length , default */

short PORT = 80;                        /* web server listening port , default */

int BACKLOG = 10;                        /* web server tcp listen queue length , default */

size_t RECV_BUFFER_SIZE = 1024;            /* web server receive buffer size , default */

size_t SEND_BUFFER_SIZE = 1050000;        /* web server send buffer size , default */

size_t TIME_BUFFER_SIZE = 40;            /* web server time buffer size , default */

size_t URI_SIZE = 128;                    /* web server uri buffer size , default */

size_t MIME_BUFFER_SIZE = 20;            /* web server mime type buffer size , default */

size_t CONTENT_ENCODING_SIZE = 40;        /* web server content-encoding buffer size , default */

size_t FILE_MAX_SIZE = 1048576;            /* web server file buffer size , default */

char WEB_ROOT_PATH[WEB_ROOT_PATH_LEN + 1];    /* web server root directory */

int config()
{
    FILE *fp = NULL;
    if( (fp = fopen(CONFIG_PATH, "r")) == NULL)
    {
        printf("could not to open %s\n", CONFIG_PATH);
        return -1;
    }
    char line_buf[CONFIG_LINE_MAX_LENTH];
    memset(line_buf, '\0', CONFIG_LINE_MAX_LENTH);
    char *line = NULL;
    while((line = fgets(line_buf, CONFIG_LINE_MAX_LENTH, fp)) != NULL)
    {
        if((*line == '#') || (*line == ' '))
            continue;
            
        if(!strncmp(line, "PORT=", 5))
        {
            if(sscanf(line + 5, "%hd;", &PORT) != 1)
            {
                perror("sscanf PORT,use default");
            }
            printf("Read config: PORT=%d\n",(int)PORT);
            continue;
        }
        if(!strncmp(line, "BACKLOG=", 8))
        {
            if(sscanf(line + 8, "%u;", &BACKLOG) != 1)
            {
                perror("sscanf BACKLOG,use default");
            }
            continue;
        }
        if(!strncmp(line, "RECV_BUFFER_SIZE=", 17))
        {
            if(sscanf(line + 17, "%u;", &RECV_BUFFER_SIZE) != 1)
            {
                perror("sscanf RECV_BUFFER_SIZE,use default");
            }
            continue;
        }
        if(!strncmp(line, "SEND_BUFFER_SIZE=", 17))
        {
            if(sscanf(line + 17, "%u;", &SEND_BUFFER_SIZE) != 1)
            {
                perror("sscanf SEND_BUFFER_SIZE,use default");
            }
            continue;
        }
        if(!strncmp(line, "TIME_BUFFER_SIZE=", 17))
        {
            if(sscanf(line + 17, "%u;", &TIME_BUFFER_SIZE) != 1)
            {
                perror("sscanf TIME_BUFFER_SIZE,use default");
            }
            continue;
        }
        if(!strncmp(line, "URI_SIZE=", 9))
        {
            if(sscanf(line + 9, "%u;", &URI_SIZE) != 1)
            {
                perror("sscanf URI_SIZE,use default");
            }
            continue;
        }
        if(!strncmp(line, "MIME_BUFFER_SIZE=", 17))
        {
            if(sscanf(line + 17, "%u;", &MIME_BUFFER_SIZE) != 1)
            {
                perror("sscanf MIME_BUFFER_SIZE,use default");
            }
            continue;
        }
        if(!strncmp(line, "CONTENT_ENCODING_SIZE=", 22))
        {
            if(sscanf(line + 22, "%u;", &CONTENT_ENCODING_SIZE) != 1)
            {
                perror("sscanf CONTENT_ENCODING_SIZE,use default");
            }
            continue;
        }
        if(!strncmp(line, "FILE_MAX_SIZE=", 14))
        {
            if(sscanf(line + 14, "%u;", &FILE_MAX_SIZE) != 1)
            {
                perror("sscanf FILE_MAX_SIZE,use default");
            }
            continue;
        }
        if(!strncmp(line, "WEB_ROOT_PATH=", 14))
        {
            memset(WEB_ROOT_PATH, 0, sizeof(WEB_ROOT_PATH));
            if(sscanf(line + 14, "%s", WEB_ROOT_PATH) != 1)    
            {
                perror("sscanf WEB_ROOT_PATH, use default");    
                strcpy(WEB_ROOT_PATH, "../www");                                    /* set to default */
            }

            continue;
        }
        
    }
    
    fclose(fp);
    return 0;
}


get_time.h

/* file: get_time.h */
#ifndef GET_TIME_H
#define GET_TIME_H

int get_time_str(char *time_buf);

#endif


get_time.c

/* file: get_time.c */
#include <time.h>
#include <stdio.h>
#include <string.h>
#include "get_time.h"



/* get the time on server,
   return: the length of ascii string of time , -1 on error
   argument: time_buf the buffer to store time_string
   time format:    Thu, 08 Oct 2009 08:49:28 UTC
*/


int get_time_str(char *time_buf)
{
    extern size_t TIME_BUFFER_SIZE;
    time_t    now_sec;
    struct tm    time_now;
    if(    time(&now_sec) == -1)
    {
        perror("time() in get_time.c");
        return -1;
    }
    if((gmtime_r(&now_sec, &time_now)) == NULL)
    {
        perror("localtime in get_time.c");
        return -1;
    }
    int strlen = -1;
    /* strftime maybe not thread safe,but i am not sure */
    if((strlen = strftime(time_buf, TIME_BUFFER_SIZE, "Date:%a, %d %b %Y %X UTC\r\n", &time_now)) == 0)    
    {
        perror("strftime in get_time.c");
        return -1;
    }
    return strlen;
}



http_session.h

/* file: http_session.h */
#ifndef HTTP_SESSION_H
#define HTTP_SESSION_H

#include <netinet/in.h>


#define TIME_OUT_SEC        600            /* select timeout of secend */
#define TIME_OUT_USEC        0            /* select timeout of usecend */

int http_session(int connect_fd);

#endif


http_session.c

/* file: http_session.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netinet/in.h>
#include "http_session.h"
#include "thread.h"


int http_session(int connect_fd)
{
#ifdef DEBUG
    printf("run in http_session()\n");
#endif

    extern size_t RECV_BUFFER_SIZE;
    
    char *recv_buf = NULL;
    if( (recv_buf = (char*)calloc(RECV_BUFFER_SIZE + 1, sizeof(char))) == NULL)        /* server socket receive buffer */
    {
#ifdef DEBUG
        printf("malloc recv_buf");
#endif
        return -1;
    }
    
    int maxfd = connect_fd + 1;
    fd_set read_set;
    FD_ZERO(&read_set);
    FD_SET(connect_fd, &read_set);
    
    struct timeval timeout;
    timeout.tv_sec = TIME_OUT_SEC;
    timeout.tv_usec = TIME_OUT_USEC;
    
    int res = 0;
    int read_bytes = 0;
    
    while(1)
    {
        
        res = select(maxfd, &read_set, NULL, NULL, &timeout);
        switch(res)
        {
            case -1:
             perror("select() error. in http_sesseion.c");
             close(connect_fd);
             return -1;
             break;
            case 0:            /* time out, continue to select */
#ifdef DEBUG
             printf("select timeout, exit the http session\n");
#endif
             return -1;        /* select timeout, client may shut down or other reason */
             break;
            default:        /* there are some file-descriptor's status changed */
             if(FD_ISSET(connect_fd, &read_set))
             {
                #ifdef DEBUG
                    printf("in select() there are some data to read\n");
                #endif
                memset(recv_buf, 0, sizeof(recv_buf));
                if((read_bytes = recv(connect_fd, recv_buf, RECV_BUFFER_SIZE, 0)) == 0)    /* receive buffer is large enough,so can read all http requst one time */
                {
                    /* client normaly close the connection */
                    return 0;
                }
                else if(read_bytes > 0)        /* there are some data from client */
                {
#ifdef DEBUG
                    printf("in select() pass data to thread\n");
#endif
                    if( new_thread(connect_fd, recv_buf) == -1)
                    {
#ifdef DEBUG
                        printf("new_thread error\n");
#endif
                        return -1;
                    }    
                }
                        
             }

        }

    }

    return 0;
}


http_header.h

/* file: http_header.h */
#ifndef HTTP_HEADER_H
#define    HTTP_HEADER_H

#define    FILE_OK            200
#define    FILE_FORBIDEN        403            /* there are no access permission*/
#define FILE_NOT_FOUND        404            /* file not found on server */
#define    UNALLOW_METHOD        405            /* un allow http request method*/
#define FILE_TOO_LARGE        413            /* file is too large */
#define    URI_TOO_LONG        414            /* */
#define    UNSUPPORT_MIME_TYPE    415
#define    SERVER_ERROR        500
#define    UNSUPPORT_HTTP_VERSION    505



#define ALLOW                "Allow:GET\r\n"    /* the server allow GET request method*/
#define    SERVER                "Server:Mutu(0.1 Beta)/Unix\r\n"

int set_header();

#endif


http_header.c

/* file: http_header.c */
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include "http_header.h"
#include "get_time.h"
#include "thread.h"

extern pthread_key_t key;

int set_header()
{
    struct HTTP_TSD *tsd = NULL;
    if((tsd = (struct HTTP_TSD *)pthread_getspecific(key)) == NULL)
    {
#ifdef DEBUG
        printf("getspecific in set_header");
#endif
        return -1;
    }
    if(tsd->time_buf == NULL)
    {
#ifdef DEBUG
        printf("NULL of tsd->time_buf in set_header");
#endif
        return -1;
    }
    if(tsd->send_buf == NULL)
    {
#ifdef DEBUG
        printf("NULL of tsd->send_buf in set_header");
#endif
        return -1;
    }
    if(tsd->mime_type == NULL)
    {
#ifdef DEBUG
        printf("NULL of tsd->mime_type in set_header");
#endif
        return -1;
    }
    if(tsd->file_buf == NULL)
    {
#ifdef DEBUG
        printf("NULL of tsd->uri_http in set_header");
#endif
        return -1;
    }
    
    /* set http response header */
    if(get_time_str(tsd->time_buf) == -1)
    {
#ifdef DEBUG
        printf("get_time_str error in set_header");
#endif
        return -1;
    }
    
    register int index = 0;
    register int strsize = 0;
    switch(tsd->http_status)
    {
        
        case FILE_OK:
        case FILE_NOT_FOUND:
            memcpy(tsd->send_buf, "HTTP/1.1 200 OK\r\n", 17);    /* response header */
            strsize = strlen(SERVER);
            index += 17;
            memcpy(tsd->send_buf + index, SERVER, strsize);                /* Server field */
            index += strsize;
            strsize = strlen(tsd->time_buf);
            memcpy(tsd->send_buf + index, tsd->time_buf, strsize);            /* Date field */
            index += strsize;
        
            memcpy(tsd->send_buf + index, "Content-Type:", 13);
            index += 13;
            strsize = strlen(tsd->mime_type);
            memcpy(tsd->send_buf + index, tsd->mime_type, strsize);
            index += strsize;

            memcpy(tsd->send_buf + index, "\r\n", 2);
            index += 2;
            memcpy(tsd->send_buf + index, "Content-Length:", 15);
            index += 15;
            
            sprintf( (char *)tsd->send_buf + index, "%d\r\n\r\n" , tsd->file_size);
            


            index += strlen((char *)(tsd->send_buf + index));
            memcpy(tsd->send_buf + index, tsd->file_buf, tsd->file_size);



            return index + tsd->file_size;
            
        /* the client browse requair a not surported mime type */
        case UNSUPPORT_MIME_TYPE:
            strcat((char *)tsd->send_buf, "HTTP/1.1 415 UNSUPPORT_MIME_TYPE\r\n");
            strcat((char *)tsd->send_buf, SERVER);
            strcat((char *)tsd->send_buf, tsd->time_buf);
            strcat((char *)tsd->send_buf, ALLOW);
            strcat((char *)tsd->send_buf, "Content-Type: text/plain\r\n");
            strcat((char *)tsd->send_buf, "Content-Lenght:0\r\n");
            strcat((char *)tsd->send_buf, "\r\n");
            return 0;
            
        /* some unknown error happened on server */
        default:
            strcat((char *)tsd->send_buf, "HTTP/1.1 500 SERVER_ERROR\r\n");
            strcat((char *)tsd->send_buf, SERVER);
            strcat((char *)tsd->send_buf, tsd->time_buf);
            strcat((char *)tsd->send_buf, ALLOW);
            strcat((char *)tsd->send_buf, "Content-Type: text/plain\r\n");
            strcat((char *)tsd->send_buf, "Content-Lenght:0\r\n");
            strcat((char *)tsd->send_buf, "\r\n");
            return 0;
            
    }        
    
    return 0;
}


                               下一页

阅读(3360) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~