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


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

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

    直接代码 一切皆在代码中 

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

      头文件: 

     1 #ifndef _HEAD_H_
     2 #define _HEAD_H_
     3 
     4 #include<stdio.h>
     5 #include<string.h>
     6 #include<stdlib.h>
     7 #include<time.h>
     8 #include<math.h>
     9 #include <fcntl.h>
    10 #include <ctype.h>
    11 #include <dirent.h>
    12 #include <unistd.h>
    13 #include <event2/event.h>
    14 #include <event2/listener.h>
    15 #include <event2/bufferevent.h>
    16 #include <sys/socket.h>
    17 #include <sys/types.h>
    18 #include <sys/stat.h>
    19 #include <arpa/inet.h>
    20 
    21 int judge_type_dir_or_nondir(const char* name);
    22 int send_dir_asheml(struct bufferevent *bufev, char *dirname, void *arg);
    23 struct evconnlistener* libev_start(struct event_base*base, const char* Ip,int Port);
    24 int send_html_head(struct bufferevent* bufev, int stat_no, const char* stat_desc, char* type);
    25 const char *get_file_type(const char *name);
    26 int send_file(struct bufferevent *bufev,char* file);
    27 
    28 #endif
    View Code

      主函数文件:

     1 // File Name: main.c
     2 // Author: jiujue
     3 // Created Time: 2019年04月05日 星期五 19时54分48秒
     4 
     5 #include "head.h"
     6 
     7 int main(int argc, const char* argv[])
     8 {
     9     if(argc < 4)
    10     {
    11         printf("argument not enough
    eg:./app Ip Port sourceDir
    ");
    12         exit(1);
    13     }
    14 
    15 
    16     int ret = chdir(argv[3]);
    17     if(-1 == ret)
    18     {
    19         perror("chdir error");
    20         exit(1);
    21     }
    22     else
    23     {
    24         printf("chdir successful,to -> %s
    ",argv[3]);
    25     }
    26 
    27     struct event_base* base = event_base_new();
    28 
    29     struct evconnlistener* listen = libev_start(base,argv[1],atoi(argv[2]));
    30 
    31     event_base_dispatch(base);
    32 
    33     evconnlistener_free(listen);
    34     event_base_free(base);
    35 
    36     return 0 ;
    37 }
    View Code

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

      1 // File Name: _libev.c
      2 // Author: jiujue
      3 // Created Time: 2019年04月05日 星期五 19时54分08秒
      4 #include "head.h"
      5 #define PATH_OF_404_ "404.html"
      6 
      7 void read_cb(struct bufferevent* bufev, void* arg)
      8 {
      9     printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<,,Happen read_cb
    ");
     10     char buf[1024] = {0}, method[12] = {0}, path[1024] = {0}, protocol[12] = {0};
     11     bufferevent_read(bufev,buf,sizeof(buf));
     12     //printf("-----------------------recv http request :%s
    ",buf);
     13     sscanf(buf,"%s %s %s",method,path,protocol);
     14     char *file = path+1;
     15     if(0 == (strcmp(path,"/") ) )
     16     {
     17         file = (char*)"./";
     18     }
     19     
     20     int isFile = judge_type_dir_or_nondir(file);
     21     printf("fffffffff          is %d 
    ",isFile);
     22     if(0 == isFile)
     23     {//is palin file
     24         printf("send file <name>>%s
    ",file);
     25         send_file(bufev,file);
     26     }
     27     if(isFile == 1){//is dir
     28         printf("send dir <name>>%s
    ",file);
     29         send_html_head(bufev,200,"OK",(char*)"text/html");
     30         send_dir_asheml(bufev,file,NULL);
     31     }
     32     else if(-1 == isFile)
     33     {//is not found file or directory
     34         printf("send 404 <name>>%s
    ",file);
     35         send_file(bufev,PATH_OF_404_);
     36     }
     37 
     38 }
     39 
     40 void write_cb(struct bufferevent* bufev, void* arg)
     41 {
     42     struct sockaddr_in *cli = (struct sockaddr_in*)arg;
     43     char buf[1024] = {0};
     44     printf("Sent respond to cli,Ip ->%s and Port ->%d
    ",
     45             inet_ntop(AF_INET,&(cli->sin_addr.s_addr), buf,sizeof(buf)), 
     46             ntohs(cli->sin_port) );
     47 }
     48 
     49 void event_cb(struct bufferevent* bufev, short ev, void* arg)
     50 {
     51     printf("event_cb successful
    ");
     52     if(ev & BEV_EVENT_EOF)
     53     {
     54         struct sockaddr_in *cli = (struct sockaddr_in*)arg;
     55         char buf[1024] = {0};
     56         printf("Have client disconnect, Ip ->%s and Port ->%d
    ",
     57                 inet_ntop(AF_INET,&(cli->sin_addr.s_addr), buf,sizeof(buf)), 
     58                 ntohs(cli->sin_port) );
     59 
     60     }
     61     if(ev & BEV_EVENT_ERROR )
     62     {
     63         printf("******************************** Happy Error******************************
    ");
     64     }
     65     bufferevent_free(bufev);
     66 }
     67 
     68 void listener_cb(struct evconnlistener *listener, 
     69         evutil_socket_t fd, struct sockaddr* cli, 
     70         int cli_len, void* arg)
     71 {
     72 
     73     printf("<<<<<<<<<<<<<<<<<<<<,,,,,,,,, listener_cb successful
    ");
     74     struct event_base* base = (struct event_base*)arg;
     75 
     76     struct bufferevent* bufev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
     77     bufferevent_setcb(bufev, read_cb, write_cb, event_cb,cli);
     78     bufferevent_enable(bufev,EV_READ);
     79 
     80 }
     81 
     82 
     83 struct evconnlistener* libev_start(struct event_base*base, const char* Ip,int Port)
     84 {
     85 
     86     struct sockaddr_in ser;
     87     ser.sin_family = AF_INET;
     88     ser.sin_port = htons(Port);
     89     inet_pton(AF_INET,Ip,&(ser.sin_addr.s_addr));
     90 
     91     struct evconnlistener* ret =  evconnlistener_new_bind(base, listener_cb, base, 
     92             LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, 128, 
     93             (struct sockaddr *)&ser, sizeof(ser));
     94 
     95     if(ret == NULL)
     96     {
     97         return NULL;
     98     }
     99 
    100     return ret;
    101 }
    View Code

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

      文件的:

     1 #include "head.h"
     2 
     3 int send_file(struct bufferevent *bufev,char* file)
     4 {
     5 
     6 
     7     int ffd = open(file,O_RDONLY);
     8     if(-1 == ffd)
     9     {
    10         printf("sourceFilePath : %s
    ",file);
    11         perror("open sourceFile error");
    12 
    13     }
    14 
    15     char file_read_buf[1024];
    16     int read_len = 0;
    17 
    18     char * type = get_file_type(file);
    19 
    20 
    21     send_html_head(bufev,200, "OK", type);
    22 
    23     while((read_len=read(ffd, file_read_buf,sizeof(file_read_buf))) > 0)
    24     {
    25         if(0 == read_len)
    26         {
    27             break;
    28         }
    29         bufferevent_write(bufev,file_read_buf,read_len);;
    30         file_read_buf[strlen(file_read_buf)+1] = '
    ';
    31         printf("send message :%s
    ",file_read_buf);
    32         memset(file_read_buf,0,sizeof(file_read_buf));
    33     }
    34 
    35     printf("close ...
    ");
    36     close(ffd);
    37     return 0;
    38 }
    View Code

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

     1 // File Name: _send_dir.c
     2 // Author: jiujue
     3 // Created Time: 2019年04月05日 星期五 19时27分18秒
     4 
     5 #include "head.h"
     6 #define MAXFORGTML_ 4096
     7 
     8 
     9 int send_dir_asheml(struct bufferevent *bufev, char *dirname, void* arg)
    10 {
    11     printf("******************send_dir name is %s
    ",dirname);
    12 
    13     char* buf_dir_html = (char *)malloc(MAXFORGTML_); 
    14     struct dirent **ptr;
    15     int dir_total_num = scandir(dirname,&ptr,NULL,alphasort);
    16     
    17     //html head
    18     sprintf(buf_dir_html,"<!doctype HTML>
    19                                 <html>
    20                                     <head>
    21                                         <title>Curent dir:%s</title>
    22                                     </head>
    23                                     <body>
    24                                         <h1>Curretn Dir Content:%s </h1>
    25                                         <table>
    26                                             <tr>
    27                                                 <td>Name</td><td>Size</td><td>Type</td>
    28                                             </tr>",dirname,dirname);
    29     bufferevent_write(bufev,buf_dir_html,strlen(buf_dir_html));
    30 
    31     for(int i=0;i<dir_total_num;++i)
    32     {
    33         char buf_current_name[1024] = {0};
    34         if( 0 == strcmp(dirname,"./"))
    35         {
    36             sprintf(buf_current_name,"%s%s",dirname,ptr[i]->d_name);
    37         }
    38         else
    39         {
    40             sprintf(buf_current_name,"%s/%s",dirname,ptr[i]->d_name);
    41         }
    42         printf("++++++++++++++++++++send cur dir <name>>%s
    ",buf_current_name);
    43         struct stat st;
    44         memset(&st,0,sizeof(st));
    45         stat(buf_current_name,&st);
    46     
    47         sprintf(buf_dir_html,
    48                                         "<tr>
    49                                             <td><a href="%s">%s</a></td>
    50                                             <td>%ld</td>
    51                                             <td>%s</td>
    52                                         </tr>",
    53                 buf_current_name,
    54                 ptr[i]->d_name,st.st_size,
    55                 judge_type_dir_or_nondir(buf_current_name)!= 0?"dir":"plain file");
    56         bufferevent_write(bufev,buf_dir_html,strlen(buf_dir_html));
    57         memset((char*)buf_dir_html,0,sizeof(buf_dir_html));
    58     }
    59 
    60     //html end
    61     sprintf(buf_dir_html,
    62                                         "</table>
    63                                     </body>
    64                                 </html>");
    65     bufferevent_write(bufev,buf_dir_html,strlen(buf_dir_html));
    66     bufferevent_write(bufev,"
    ",2);
    67 
    68     free(buf_dir_html);
    69     return 0;
    70 }
    View Code

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

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

     1 // File Name: _get_file_type.c
     2 // Author: jiujue
     3 // Created Time: 2019年04月06日 星期六 19时14分07秒
     4 
     5 
     6 #include "head.h"
     7 
     8 const char *get_file_type(const char *name)
     9 {
    10     char* dot;
    11 
    12     
    13     dot = strrchr(name, '.');   
    14     if (dot == NULL)
    15         return "text/plain; charset=utf-8";
    16     if (strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0)
    17         return "text/html; charset=utf-8";
    18     if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
    19         return "image/jpeg";
    20     if (strcmp(dot, ".gif") == 0)
    21         return "image/gif";
    22     if (strcmp(dot, ".png") == 0)
    23         return "image/png";
    24     if (strcmp(dot, ".css") == 0)
    25         return "text/css";
    26     if (strcmp(dot, ".au") == 0)
    27         return "audio/basic";
    28     if (strcmp( dot, ".wav" ) == 0)
    29         return "audio/wav";
    30     if (strcmp(dot, ".avi") == 0)
    31         return "video/x-msvideo";
    32     if (strcmp(dot, ".mov") == 0 || strcmp(dot, ".qt") == 0)
    33         return "video/quicktime";
    34     if (strcmp(dot, ".mpeg") == 0 || strcmp(dot, ".mpe") == 0)
    35         return "video/mpeg";
    36     if (strcmp(dot, ".vrml") == 0 || strcmp(dot, ".wrl") == 0)
    37         return "model/vrml";
    38     if (strcmp(dot, ".midi") == 0 || strcmp(dot, ".mid") == 0)
    39         return "audio/midi";
    40     if (strcmp(dot, ".mp3") == 0)
    41         return "audio/mpeg";
    42     if (strcmp(dot, ".ogg") == 0)
    43         return "application/ogg";
    44     if (strcmp(dot, ".pac") == 0)
    45         return "application/x-ns-proxy-autoconfig";
    46 
    47     return "text/plain; charset=utf-8";
    48 }
    View Code

      判断是否为目录:

     1 // File Name: _judge_type.c
     2 // Author: jiujue
     3 // Created Time: 2019年04月05日 星期五 20时54分34秒
     4 
     5 #include "head.h"
     6 
     7 int judge_type_dir_or_nondir(const char* name)
     8 {
     9     struct stat st;
    10     int ret = stat(name,&st);
    11     if(-1 == ret)
    12     {
    13         return -1;
    14     }
    15     if(S_ISREG(st.st_mode))
    16     {
    17         return 0;
    18     }
    19     if(S_ISDIR(st.st_mode))
    20     {
    21         return 1;
    22     }
    23     else
    24     {
    25         return 2;
    26     }
    27 
    28 }
    29 
    30 
    31 #if 0
    32 int main(int argc,char* argv[])
    33 {
    34     int ret =  judge_type_dir_or_nondir(argv[1]);
    35     if(ret == 1)
    36     {
    37         printf("is dir ");
    38     }
    39     if(ret == 0)
    40     {
    41         printf("is  file");
    42     }
    43     return 0;
    44 }
    45 #endif
    View Code

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

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

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

  • 相关阅读:
    linux tftp 服务
    AtomicInteger
    深入理解JVM(三)——垃圾收集策略具体解释
    Android 虚拟现实(virtual reality)入门指南
    Java千百问_05面向对象(005)_接口和抽象类有什么差别
    postman发送json格式的post请求
    什么是Session分布式共享
    如何设计一个单点登录系统(3)?
    如何设计一个单点登录系统(2)?
    如何设计一个单点登录系统(1)?
  • 原文地址:https://www.cnblogs.com/jiujue/p/10707153.html
Copyright © 2020-2023  润新知