基于 libevent 开源框架实现的 web 服务器

时间:2023-03-08 21:54:51

/* 原创文章 转载请附上原链接: https://www.cnblogs.com/jiujue/p/10707153.html   */

自己实现的如有缺漏欢迎提出

直接代码 一切皆在代码中

首先是 主函数文件 和 头文件

  头文件: 

 #ifndef _HEAD_H_
#define _HEAD_H_ #include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>
#include <fcntl.h>
#include <ctype.h>
#include <dirent.h>
#include <unistd.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h> int judge_type_dir_or_nondir(const char* name);
int send_dir_asheml(struct bufferevent *bufev, char *dirname, void *arg);
struct evconnlistener* libev_start(struct event_base*base, const char* Ip,int Port);
int send_html_head(struct bufferevent* bufev, int stat_no, const char* stat_desc, char* type);
const char *get_file_type(const char *name);
int send_file(struct bufferevent *bufev,char* file); #endif

  主函数文件:

 // File Name: main.c
// Author: jiujue
// Created Time: 2019年04月05日 星期五 19时54分48秒 #include "head.h" int main(int argc, const char* argv[])
{
if(argc < )
{
printf("argument not enough\neg:./app Ip Port sourceDir\n");
exit();
} int ret = chdir(argv[]);
if(- == ret)
{
perror("chdir error");
exit();
}
else
{
printf("chdir successful,to -> %s\n",argv[]);
} struct event_base* base = event_base_new(); struct evconnlistener* listen = libev_start(base,argv[],atoi(argv[])); event_base_dispatch(base); evconnlistener_free(listen);
event_base_free(base); return ;
}

接下来是 调用libevent框架了 (重头戏来了 注意回调的设置哦):

 // File Name: _libev.c
// Author: jiujue
// Created Time: 2019年04月05日 星期五 19时54分08秒
#include "head.h"
#define PATH_OF_404_ "404.html" void read_cb(struct bufferevent* bufev, void* arg)
{
printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<,,Happen read_cb\n");
char buf[] = {}, method[] = {}, path[] = {}, protocol[] = {};
bufferevent_read(bufev,buf,sizeof(buf));
//printf("-----------------------recv http request :%s\n",buf);
sscanf(buf,"%s %s %s",method,path,protocol);
char *file = path+;
if( == (strcmp(path,"/") ) )
{
file = (char*)"./";
} int isFile = judge_type_dir_or_nondir(file);
printf("fffffffff is %d \n",isFile);
if( == isFile)
{//is palin file
printf("send file <name>>%s\n",file);
send_file(bufev,file);
}
if(isFile == ){//is dir
printf("send dir <name>>%s\n",file);
send_html_head(bufev,,"OK",(char*)"text/html");
send_dir_asheml(bufev,file,NULL);
}
else if(- == isFile)
{//is not found file or directory
printf("send 404 <name>>%s\n",file);
send_file(bufev,PATH_OF_404_);
} } void write_cb(struct bufferevent* bufev, void* arg)
{
struct sockaddr_in *cli = (struct sockaddr_in*)arg;
char buf[] = {};
printf("Sent respond to cli,Ip ->%s and Port ->%d\n",
inet_ntop(AF_INET,&(cli->sin_addr.s_addr), buf,sizeof(buf)),
ntohs(cli->sin_port) );
} void event_cb(struct bufferevent* bufev, short ev, void* arg)
{
printf("event_cb successful\n");
if(ev & BEV_EVENT_EOF)
{
struct sockaddr_in *cli = (struct sockaddr_in*)arg;
char buf[] = {};
printf("Have client disconnect, Ip ->%s and Port ->%d\n",
inet_ntop(AF_INET,&(cli->sin_addr.s_addr), buf,sizeof(buf)),
ntohs(cli->sin_port) ); }
if(ev & BEV_EVENT_ERROR )
{
printf("******************************** Happy Error******************************\n");
}
bufferevent_free(bufev);
} void listener_cb(struct evconnlistener *listener,
evutil_socket_t fd, struct sockaddr* cli,
int cli_len, void* arg)
{ printf("<<<<<<<<<<<<<<<<<<<<,,,,,,,,, listener_cb successful\n");
struct event_base* base = (struct event_base*)arg; struct bufferevent* bufev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bufev, read_cb, write_cb, event_cb,cli);
bufferevent_enable(bufev,EV_READ); } struct evconnlistener* libev_start(struct event_base*base, const char* Ip,int Port)
{ struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(Port);
inet_pton(AF_INET,Ip,&(ser.sin_addr.s_addr)); struct evconnlistener* ret = evconnlistener_new_bind(base, listener_cb, base,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, ,
(struct sockaddr *)&ser, sizeof(ser)); if(ret == NULL)
{
return NULL;
} return ret;
}

然后是 发送文件和目录的回调“

  文件的:

 #include "head.h"

 int send_file(struct bufferevent *bufev,char* file)
{ int ffd = open(file,O_RDONLY);
if(- == ffd)
{
printf("sourceFilePath : %s\n",file);
perror("open sourceFile error"); } char file_read_buf[];
int read_len = ; char * type = get_file_type(file); send_html_head(bufev,, "OK", type); while((read_len=read(ffd, file_read_buf,sizeof(file_read_buf))) > )
{
if( == read_len)
{
break;
}
bufferevent_write(bufev,file_read_buf,read_len);;
file_read_buf[strlen(file_read_buf)+] = '\n';
printf("send message :%s\n",file_read_buf);
memset(file_read_buf,,sizeof(file_read_buf));
} printf("close ...\n");
close(ffd);
return ;
}

  目录的(这里拼接网页的时候要细心 非常细心的那种 ):

 // File Name: _send_dir.c
// Author: jiujue
// Created Time: 2019年04月05日 星期五 19时27分18秒 #include "head.h"
#define MAXFORGTML_ 4096 int send_dir_asheml(struct bufferevent *bufev, char *dirname, void* arg)
{
printf("******************send_dir name is %s\n",dirname); char* buf_dir_html = (char *)malloc(MAXFORGTML_);
struct dirent **ptr;
int dir_total_num = scandir(dirname,&ptr,NULL,alphasort); //html head
sprintf(buf_dir_html,"<!doctype HTML>\
<html>\
<head>\
<title>Curent dir:%s</title>\
</head>\
<body>\
<h1>Curretn Dir Content:%s </h1>\
<table>\
<tr>\
<td>Name</td><td>Size</td><td>Type</td>\
</tr>",dirname,dirname);
bufferevent_write(bufev,buf_dir_html,strlen(buf_dir_html)); for(int i=;i<dir_total_num;++i)
{
char buf_current_name[] = {};
if( == strcmp(dirname,"./"))
{
sprintf(buf_current_name,"%s%s",dirname,ptr[i]->d_name);
}
else
{
sprintf(buf_current_name,"%s/%s",dirname,ptr[i]->d_name);
}
printf("++++++++++++++++++++send cur dir <name>>%s\n",buf_current_name);
struct stat st;
memset(&st,,sizeof(st));
stat(buf_current_name,&st); sprintf(buf_dir_html,
"<tr>\
<td><a href=\"%s\">%s</a></td>\
<td>%ld</td>\
<td>%s</td>\
</tr>",
buf_current_name,
ptr[i]->d_name,st.st_size,
judge_type_dir_or_nondir(buf_current_name)!= ?"dir":"plain file");
bufferevent_write(bufev,buf_dir_html,strlen(buf_dir_html));
memset((char*)buf_dir_html,,sizeof(buf_dir_html));
} //html end
sprintf(buf_dir_html,
"</table>\
</body>\
</html>");
bufferevent_write(bufev,buf_dir_html,strlen(buf_dir_html));
bufferevent_write(bufev,"\r\n",); free(buf_dir_html);
return ;
}

最后就是一些小函数了: 判断文件类型 和是否为目录:

  判断文件类型(这里如果出问题 打开图片等时会出现问题):

 // File Name: _get_file_type.c
// Author: jiujue
// Created Time: 2019年04月06日 星期六 19时14分07秒 #include "head.h" const char *get_file_type(const char *name)
{
char* dot; dot = strrchr(name, '.');
if (dot == NULL)
return "text/plain; charset=utf-8";
if (strcmp(dot, ".html") == || strcmp(dot, ".htm") == )
return "text/html; charset=utf-8";
if (strcmp(dot, ".jpg") == || strcmp(dot, ".jpeg") == )
return "image/jpeg";
if (strcmp(dot, ".gif") == )
return "image/gif";
if (strcmp(dot, ".png") == )
return "image/png";
if (strcmp(dot, ".css") == )
return "text/css";
if (strcmp(dot, ".au") == )
return "audio/basic";
if (strcmp( dot, ".wav" ) == )
return "audio/wav";
if (strcmp(dot, ".avi") == )
return "video/x-msvideo";
if (strcmp(dot, ".mov") == || strcmp(dot, ".qt") == )
return "video/quicktime";
if (strcmp(dot, ".mpeg") == || strcmp(dot, ".mpe") == )
return "video/mpeg";
if (strcmp(dot, ".vrml") == || strcmp(dot, ".wrl") == )
return "model/vrml";
if (strcmp(dot, ".midi") == || strcmp(dot, ".mid") == )
return "audio/midi";
if (strcmp(dot, ".mp3") == )
return "audio/mpeg";
if (strcmp(dot, ".ogg") == )
return "application/ogg";
if (strcmp(dot, ".pac") == )
return "application/x-ns-proxy-autoconfig"; return "text/plain; charset=utf-8";
}

  判断是否为目录:

 // File Name: _judge_type.c
// Author: jiujue
// Created Time: 2019年04月05日 星期五 20时54分34秒 #include "head.h" int judge_type_dir_or_nondir(const char* name)
{
struct stat st;
int ret = stat(name,&st);
if(- == ret)
{
return -;
}
if(S_ISREG(st.st_mode))
{
return ;
}
if(S_ISDIR(st.st_mode))
{
return ;
}
else
{
return ;
} } #if 0
int main(int argc,char* argv[])
{
int ret = judge_type_dir_or_nondir(argv[]);
if(ret == )
{
printf("is dir ");
}
if(ret == )
{
printf("is file");
}
return ;
}
#endif

注:以上代码已测验,基本没有问题(bug 肯定有 欢迎提出)

结语:有问题欢迎提在下方 ,本人在校学生,时间较为充裕, 有时间会回复的。

/* 原创文章 转载请附上原链接: https://www.cnblogs.com/jiujue/p/10707153.html   */