一个简单的wed服务器SHTTPD(9)————main函数文件,Makefile,头文件

主函数:

#include "lcw_shttpd.h"

//初始化时服务器的默认配置
extern struct conf_opts conf_para=
{
    "/usr/local/var/www/cgi-bin/",//CGI根目录
    "index.html",//默认文件名称
    "/usr/local/var/www/",//根文件目录
    "/etc/SHTTPD.conf",//配置文件路径和名称
    8080, //监听端口
    4, //最大客户端数量
    3,//超时时间
    2//初始化线程数量
};
struct vec _shttpd_methods[] = {
    {"GET",        3, METHOD_GET},
    {"POST",        4, METHOD_POST},
    {"PUT",        3, METHOD_PUT},
    {"DELETE",    6, METHOD_DELETE},
    {"HEAD",        4, METHOD_HEAD},
    {NULL,        0}
};
/******************************************************
函数名:sig_int(int num)
参数:
功能:SIGINT信号截取函数
*******************************************************/
static void sig_int(int num)
{
    Worker_ScheduleStop();
    return;
}
/******************************************************
函数名:
参数:
功能:SIGPIPE信号截取函数
*******************************************************/
static void sig_pipe(int num)
{
    return;
}
/******************************************************
函数名:do_listen()
参数:
功能:套接字初始化
*******************************************************/
int do_listen()
{
    struct sockaddr_in server;
    int ss = -1;
    int err = -1;
    int reuse = 1;
    int ret = -1;
    // 初始化服务器地址
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr=htonl(INADDR_ANY);
    server.sin_port = htons(conf_para.ListenPort); 
    //信号截取函数
    signal(SIGINT, sig_int);
    signal(SIGPIPE, sig_pipe);
    //生成套接字文件描述符 
    ss = socket (AF_INET, SOCK_STREAM, 0);
    if (ss == -1)
    {
        printf("socket() error
");
        ret = -1;
        goto EXITshttpd_listen;
    }
    //设置套接字地址和端口复用
    err = setsockopt (ss, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
    if (err == -1)
    {
        printf("setsockopt SO_REUSEADDR failed
");
    }
    //绑定IP和套接字描述符
    err = bind (ss, (struct sockaddr*) &server, sizeof(server));
    if (err == -1)
    {
        printf("bind() error
");
        ret = -2;
        goto EXITshttpd_listen;
    }
    //设置服务器侦听队列长度
    err = listen(ss, conf_para.MaxClient*2);
    if (err)
    {
        printf ("listen() error
");
        ret = -3;
        goto EXITshttpd_listen;
    }

    ret = ss;
EXITshttpd_listen:
    return ret;
}

int l_main()
{
    int ss = -1;
    ss = do_listen();
    return 0;
}
/******************************************************
函数名:main(int argc, char *argv[])
参数:
功能:主函数
*******************************************************/
int main(int argc, char *argv[])
{
    signal(SIGINT, sig_int);//挂接信号
    Para_Init(argc,argv);//参数初始化
    int s = do_listen();//套接字初始化
    Worker_ScheduleRun(s);//任务调度
    return 0;
}

头文件:

//start from the very beginning,and to create greatness
//@author: Chuangwei Lin
//@E-mail:979951191@qq.com
//@brief: SHTTPD服务器的实现:主要的数据结构
//配置文件的结构//
#ifndef _LCW_SHTTP_H_
#define _LCW_SHTTP_H_
#include <stdio.h>
#include <getopt.h>//getopt_long()函数所在库函数
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <netinet/in.h> // for sockaddr_in 
#include <netdb.h> // for hostent 
#include <pthread.h>
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h> // we want to catch some of these after all
#include <unistd.h> // protos for read, write, close, etc 
#include <dirent.h> // for MAXNAMLEN 
#include <limits.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <stddef.h>
#define big_int_t long
#define    URI_MAX        16384        // Default max request size    
//线程的状态值
enum
{
    WORKER_INITED,//初始化
    WORKER_RUNNING,//正在执行
    WORKER_DETACHING,//正在卸载
    WORKER_DETACHED,//已经卸载
    WORKER_IDEL//空闲
};
struct conf_opts
{
    char CGIRoot[128]; //CGI根目录
    char DefaultFile[128]; //默认文件名称
    char DocumentRoot[128]; //根文件目录
    char ConfigFile[128]; //配置文件路径和名称
    int ListenPort; //监听端口
    int MaxClient; //最大客户端数量
    int TimeOut; //超时时间
    int InitClient; //初始化线程数量
};
// HTTP协议的方法 
typedef enum SHTTPD_METHOD_TYPE{
    METHOD_GET,     //GET方法
    METHOD_POST,     //POST方法
    METHOD_PUT,     //PUT方法
    METHOD_DELETE,     //DELETE方法
    METHOD_HEAD,    //HEAD方法
    METHOD_CGI,        //CGI方法
    METHOD_NOTSUPPORT
}SHTTPD_METHOD_TYPE;

enum {HDR_DATE, HDR_INT, HDR_STRING};//HTTP头部类型

typedef struct shttpd_method
{
    SHTTPD_METHOD_TYPE type;
    int name_index;    
}shttpd_method;

typedef struct vec 
{
    char* ptr;//字符串
    int    len;//字符串长度
    SHTTPD_METHOD_TYPE type;//字符串表示类型
}vec;

struct http_header {
    int    len;        //Header name length         
    int    type;        // Header type        
    size_t    offset;    // Value placeholder        
    char* name;        // Header name        
};

 // This structure tells how HTTP headers must be parsed.
 // Used by parse_headers() function.
#define    OFFSET(x)    offsetof(struct headers, x)

union variant {
    char* v_str;
    int    v_int;
    big_int_t v_big_int;
    time_t    v_time;
    void (*v_func)(void);
    void *v_void;
    struct vec v_vec;
};

//头部结构
struct headers
 {
    union variant    cl;         //内容长度
    union variant    ct;         //内容类型
    union variant    connection; //连接状态
    union variant    ims;        //最后修改时间
    union variant    user;     //用户名称
    union variant    auth;        //权限
    union variant    useragent; //用户代理
    union variant    referer;    //参考
    union variant    cookie;        //Cookie
    union variant    location; //位置
    union variant    range;        //范围
    union variant    status;        //状态值
    union variant    transenc;    //编码类型
};

struct cgi{
    int iscgi;
    struct vec bin;
    struct vec para;    
};
struct worker_ctl;//要先声明
struct worker_opts{
    pthread_t th;            //线程的ID号
    int flags;                //线程状态
    pthread_mutex_t mutex;//线程任务互斥
    struct worker_ctl *work;//本线程的总控结构
};
struct worker_conn;//要先声明
//请求结构
struct conn_request{
    struct vec    req;//请求向量
    char *head;    //请求头部'结尾
    char *uri;    //请求URI,''结尾
    char rpath[URI_MAX];//请求文件的真实地址'结尾
    int method;    //请求类型
    //HTTP的版本信息 
    unsigned long major;//主版本 
    unsigned long minor;//副版本 
    struct headers ch;//头部结构 
    struct worker_conn *conn;//连接结构指针 
    int err;
};
//响应结构 
struct conn_response{
    struct vec    res;    //响应向量
    time_t birth_time;    //建立时间
    time_t expire_time;//超时时间
    int    status;        //响应状态值
    int    cl;            //响应内容长度
    int fd;        //请求文件描述符
    struct stat fsate;    //请求文件状态
    struct worker_conn *conn;//连接结构指针    
};
struct worker_conn 
{
    #define K 1024
    char dreq[16*K];    //请求缓冲区
    char dres[16*K];    //响应缓冲区
    int    cs;            //客户端套接字文件描述符
    int    to;            //客户端无响应时间超时退出时间
    struct conn_response con_res;
    struct conn_request con_req;
    struct worker_ctl *work;    //本线程的总控结构
};

struct worker_ctl
{
    struct worker_opts opts;//用于表示线程的状态
    struct worker_conn conn;//用于表示客户端请求的状态和值
};

//文件内容的类型格式
struct mine_type{
    char* extension;//扩展名
    int type;//类型
    int    ext_len;//扩展名长度
    char* mime_type;//内容类型
};


void Para_Init(int argc, char *argv[]);
int Request_Parse(struct worker_ctl *wctl);
int Request_Handle(struct worker_ctl* wctl);

int Worker_ScheduleRun();
int Worker_ScheduleStop();
void Method_Do(struct worker_ctl *wctl);
void uri_parse(char *src, int len);
struct mine_type* Mine_Type(char *uri, int len, struct worker_ctl *wctl);



#define DBGPRINT printf
#endif

Makefile:

CFLAGS = -Wall -g
LIBS = -lpthread
TARGET = lcw_shttpd
RM = rm -f 
OBJS = lcw_shttpd_parameters.o lcw_shttpd.o lcw_shttpd_worker.o lcw_shttpd_uri.o lcw_shttpd_request.o lcw_shttpd_method.o lcw_shttpd_mine.o lcw_shttpd_error.o
all:$(OBJS)
gcc -o $(TARGET) $(OBJS) $(LIBS)
clean:
$(RM) $(TARGET) $(OBJS)

书上的代码其实有很多错误,网上下载的源码好像有些地方也是有点怪怪的。编译可以通过,运行的时候,在浏览器上输入主机IP,有显示访问,dowork,但是默认的html没有运行。因为知识还不是很熟练,所以打算再熟悉下HTTP协议,以及学习另一个web服务器。

原文地址:https://www.cnblogs.com/sigma0-/p/12630518.html