libevent编写web服务器处理请求,解析文件,发送文件等基础代码

在这里插入图片描述

//通过libevent编写的web服务器
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "pub.h"
#include <event.h>
#include <event2/listener.h>
#include <dirent.h>

#define _WORK_DIR_ "%s/webpath"
#define _DIR_PREFIX_FILE_ "html/dir_header.html"
#define _DIR_TAIL_FILE_ "html/dir_tail.html"
//copy_header(bev,200,"OK",get_mime_type("ww.html"),sb.st_size);
int copy_header(struct bufferevent *bev,int op,char *msg,char *filetype,long filesize)
{
    char buf[4096]={0};
    //http应答
    sprintf(buf,"HTTP/1.1 %d %s\r\n",op,msg);
    sprintf(buf,"%sContent-Type: %s\r\n",buf,filetype);
    if(filesize >= 0){
        sprintf(buf,"%sContent-Length:%ld\r\n",buf,filesize);
    }
    strcat(buf,"\r\n");
    bufferevent_write(bev,buf,strlen(buf));
    return 0;
}
//copy_file(bev, strPath); 
int copy_file(struct bufferevent *bev,const char *strFile)
{
    int fd = open(strFile,O_RDONLY);
    char buf[1024]={0};
    int ret;
    while( (ret = read(fd,buf,sizeof(buf))) > 0 ){
        bufferevent_write(bev,buf,ret);
    }
    close(fd);
    return 0;
}
//发送目录,实际上组织一个html页面发给客户端,目录的内容作为列表显示
//send_dir(bev,strPath);
int send_dir(struct bufferevent *bev,const char *strPath)
{
    //需要拼出来一个html页面发送给客户端
    copy_file(bev,_DIR_PREFIX_FILE_);
    //send dir info 
    DIR *dir = opendir(strPath);
    if(dir == NULL){
        perror("opendir err");
        return -1;
    }
    char bufline[1024]={0};
    struct dirent *dent = NULL;
    while(dent= readdir(dir)){
        struct stat sb;
        //将状态信息存储到sb stat结构体中
        stat(dent->d_name,&sb);
        if(dent->d_type == DT_DIR){
            //目录文件 特殊处理
            //格式 <a href="dirname/">dirname</a><p>size</p><p>time</p></br>
            memset(bufline,0x00,sizeof(bufline));
            sprintf(bufline,"<li><a href='%s/'>%32s</a>%8ld</li>",dent->d_name,dent->d_name,sb.st_size);
            bufferevent_write(bev,bufline,strlen(bufline));
        }
        else if(dent->d_type == DT_REG){
            //普通文件 直接显示列表即可
            memset(bufline,0x00,sizeof(bufline));
            sprintf(bufline,"<li><a href='%s'>%32s</a>%8ld</li>",dent->d_name,dent->d_name,sb.st_size);
            bufferevent_write(bev,bufline,strlen(bufline));
        }
    }
    closedir(dir);
    copy_file(bev,_DIR_TAIL_FILE_);
    //bufferevent_free(bev);
    return 0;
}
//http_request(bev, path);//处理请求
int http_request(struct bufferevent *bev,char *path)
{
    //调用void strdecode(char *to, char *from)
    //将中文问题转码成utf-8格式的字符串
    //[/%E8%8B%A6%E7%93%9C.txt]
    printf("path转码前%s\n", path);
    strdecode(path, path);
    printf("path转码后%s\n", path);

    char *strPath = path;
    //判断是否是目录[GET]  [/pic/aa/bb/]  [HTTP/1.1]
    if(strcmp(strPath,"/") == 0 || strcmp(strPath,"/.") == 0){
        strPath = "./";
    }
    else{
        strPath = path+1;
    }
    struct stat sb;
    
    if(stat(strPath,&sb) < 0){
        //不存在 ,给404页面
        copy_header(bev,404,"NOT FOUND",get_mime_type("error.html"),-1);
        copy_file(bev,"error.html");
        return -1;
    }
    if(S_ISDIR(sb.st_mode)){
        //处理目录
        copy_header(bev,200,"OK",get_mime_type("ww.html"),sb.st_size);
        send_dir(bev,strPath);
    }
    if(S_ISREG(sb.st_mode)){
        //处理文件
        //写头
        copy_header(bev,200,"OK",get_mime_type(strPath),sb.st_size);
        //写文件内容
        copy_file(bev,strPath);
    }
    return 0;
}

void read_cb(struct bufferevent *bev, void *ctx)
{
    char buf[256]={0};
    char method[10],path[256],protocol[10];
    //ret为bufferevent_read读取的字节数
    int ret = bufferevent_read(bev, buf, sizeof(buf));
    if(ret > 0){
        // GET  /demo.html   HTTP/1.1
        sscanf(buf,"%[^ ] %[^ ] %[^ \r\n]",method,path,protocol);
        if(strcasecmp(method,"get") == 0){
            //处理客户端的请求
            char bufline[256];
            write(STDOUT_FILENO,buf,ret);
            //确保数据读完
            while( (ret = bufferevent_read(bev, bufline, sizeof(bufline)) ) > 0){
                write(STDOUT_FILENO,bufline,ret);
            }
			http_request(bev,path);//处理请求

        }
    }
}
void bevent_cb(struct bufferevent *bev, short what, void *ctx)
{
    if(what & BEV_EVENT_EOF){//客户端关闭
        printf("client closed\n");
        bufferevent_free(bev);
    }
    else if(what & BEV_EVENT_ERROR){
        printf("err to client closed\n");
        bufferevent_free(bev);
    }
    else if(what & BEV_EVENT_CONNECTED){//连接成功
        printf("client connect ok\n");
    }
}
void listen_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg)
{
	printf("new\n");
    //定义与客户端通信的bufferevent
    struct event_base *base = (struct event_base *)arg;
    struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    bufferevent_setcb(bev,read_cb,NULL,bevent_cb,base);//设置回调
    bufferevent_enable(bev,EV_READ|EV_WRITE);//启用读和写
}

int main(int argc,char *argv[])
{
    //切换目录
	char workdir[256] = {0};
	strcpy(workdir,getenv("PWD"));
	printf("%s\n",workdir);
	chdir(workdir);

    //创建根节点
    struct event_base *base = event_base_new();
    struct sockaddr_in serv;
    serv.sin_family = AF_INET;
    serv.sin_port = htons(9999);
    serv.sin_addr.s_addr = htonl(INADDR_ANY);
    //连接监听器,LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE关闭时自动释放,端口复用
    struct evconnlistener * listener =evconnlistener_new_bind(base,
        listen_cb, base, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1,(struct sockaddr *)&serv, sizeof(serv));//连接监听器
    
    event_base_dispatch(base);//循环

    event_base_free(base); //释放根节点
    evconnlistener_free(listener);//释放链接监听器
    return 0;
}

pub.c

#include "pub.h"
//通过文件名字获得文件类型
char *get_mime_type(char *name)
{
    char* dot;

    dot = strrchr(name, '.');	//自右向左查找‘.’字符;如不存在返回NULL
    if (dot == (char*)0)
        return "text/plain; charset=utf-8";
    if (strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0)
        return "text/html; charset=utf-8";
    if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
        return "image/jpeg";
    if (strcmp(dot, ".gif") == 0)
        return "image/gif";
    if (strcmp(dot, ".png") == 0)
        return "image/png";
    if (strcmp(dot, ".css") == 0)
        return "text/css";
    if (strcmp(dot, ".au") == 0)
        return "audio/basic";
    if (strcmp( dot, ".wav") == 0)
        return "audio/wav";
    if (strcmp(dot, ".avi") == 0)
        return "video/x-msvideo";
    if (strcmp(dot, ".mov") == 0 || strcmp(dot, ".qt") == 0)
        return "video/quicktime";
    if (strcmp(dot, ".mpeg") == 0 || strcmp(dot, ".mpe") == 0)
        return "video/mpeg";
    if (strcmp(dot, ".vrml") == 0 || strcmp(dot, ".wrl") == 0)
        return "model/vrml";
    if (strcmp(dot, ".midi") == 0 || strcmp(dot, ".mid") == 0)
        return "audio/midi";
    if (strcmp(dot, ".mp3") == 0)
        return "audio/mpeg";
    if (strcmp(dot, ".ogg") == 0)
        return "application/ogg";
    if (strcmp(dot, ".pac") == 0)
        return "application/x-ns-proxy-autoconfig";

    return "text/plain; charset=utf-8";
}
//获得一行数据,每行以\r\n作为结束标记
int get_line(int sock, char *buf, int size)
{
    int i = 0;
    char c = '\0';
    int n;

    while ((i < size - 1) && (c != '\n'))
    {
        n = recv(sock, &c, 1, 0);
        /* DEBUG printf("%02X\n", c); */
        if (n > 0)
        {
            if (c == '\r')
            {
                n = recv(sock, &c, 1, MSG_PEEK);//MSG_PEEK 从缓冲区读数据,但是数据不从缓冲区清除
                /* DEBUG printf("%02X\n", c); */
                if ((n > 0) && (c == '\n'))
                    recv(sock, &c, 1, 0);
                else
                    c = '\n';
            }
            buf[i] = c;
            i++;
        }
        else
            c = '\n';
    }
    buf[i] = '\0';

    return(i);
}

void strdecode(char *to, char *from)
{
    for ( ; *from != '\0'; ++to, ++from) {
        //依次判断from中 %20 三个字符
        //[/%E8%8B%A6%E7%93%9C]转化为 0xe8,0x8b,0xa6,0xe7,0x93,0x9c
        if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])) { 
            *to = hexit(from[1])*16 + hexit(from[2]);
            //移过已经处理的两个字符(%21指针指向1),表达式3的++from还会再向后移一个字符
            from += 2;                      
        } else
            *to = *from;
    }
    *to = '\0';
}

//16进制数转化为10进制, return 0不会出现
int hexit(char c)
{
    if (c >= '0' && c <= '9')
        return c - '0';
    if (c >= 'a' && c <= 'f')
        return c - 'a' + 10;
    if (c >= 'A' && c <= 'F')
        return c - 'A' + 10;

    return 0;
}

//"编码",用作回写浏览器的时候,将除字母数字及/_.-~以外的字符转义后回写。
//strencode(encoded_name, sizeof(encoded_name), name);
void strencode(char* to, size_t tosize, const char* from)
{
    int tolen;

    for (tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from) {
        if (isalnum(*from) || strchr("/_.-~", *from) != (char*)0) {
            *to = *from;
            ++to;
            ++tolen;
        } else {
            sprintf(to, "%%%02x", (int) *from & 0xff);
            to += 3;
            tolen += 3;
        }
    }
    *to = '\0';
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值