理财小白 發表於 2019-7-9 18:58:00

Linux 用libevent实现的简单http服务器

<p>Linux 用libevent实现的简单http服务器</p>
<p>main.c</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;arpa/inet.h&gt;
#include &lt;unistd.h&gt;
#include &lt;stdlib.h&gt;
#include "libev.h"
#include &lt;string.h&gt;
#include &lt;event2/event.h&gt;
#include &lt;event2/bufferevent.h&gt;
#include &lt;event2/listener.h&gt;


int main(int argc, char** argv){

// 端口
int port = atoi(argv);
// 修改进程的工作目录, 方便后续操作
int ret = chdir(argv);
if(ret == -1){
    perror("chdir error");
    exit(1);
}

struct event_base* base = NULL;
base = event_base_new();
if(base == NULL){
    perror("event_base_new");
    exit(1);
}

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

struct evconnlistener* lis =
    evconnlistener_new_bind(base, listencb, base, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1, (struct sockaddr*)&amp;addr, sizeof(addr));

if(!lis){
    perror("evconnlistener_new_bind");
    exit(1);
}

event_base_dispatch(base);
event_base_free(base);
}

</code></pre>
<p>libev.h</p>
<pre><code class="language-c">#ifndef __LIBEV__
#define __LIBEV__

#include &lt;event2/bufferevent.h&gt;
#include &lt;event2/listener.h&gt;


void listencb(struct evconnlistener* listener, evutil_socket_t fd,
              struct sockaddr* cli, int len, void* ptr);
void readcb(struct bufferevent* bev,void* ctx);
void writecb (struct bufferevent* bev,void* ctx);
void eventcb (struct bufferevent* bev,short what,void* ctx);
#endif

</code></pre>
<p>libev.c</p>
<pre><code class="language-c">#include "libev.h"
#include &lt;stdio.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include "libev.h"
#include &lt;event2/event.h&gt;
#include &lt;event2/bufferevent.h&gt;
#include &lt;event2/buffer.h&gt;
#include &lt;string.h&gt;
#include &lt;sys/stat.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;ctype.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;unistd.h&gt;
#include &lt;dirent.h&gt;

// 通过文件名获取文件的类型
const char *get_file_type(const char *name){
char* dot;

// 自右向左查找‘.’字符, 如不存在返回NULL
dot = strrchr(name, '.');   
if (dot == NULL)
    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";
}
// 16进制数转化为10进制
int hexit(char c)
{
    if (c &gt;= '0' &amp;&amp; c &lt;= '9')
      return c - '0';
    if (c &gt;= 'a' &amp;&amp; c &lt;= 'f')
      return c - 'a' + 10;
    if (c &gt;= 'A' &amp;&amp; c &lt;= 'F')
      return c - 'A' + 10;

    return 0;
}

/*
*这里的内容是处理%20之类的东西!是"解码"过程。
*%20 URL编码中的‘ ’(space)
*%21 '!' %22 '"' %23 '#' %24 '$'
*%25 '%' %26 '&amp;' %27 ''' %28 '('......
*相关知识html中的‘ ’(space)是&amp;nbsp
*/
void encode_str(char* to, int tosize, const char* from)
{
    int tolen;

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

    }
    *to = '\0';
}


void decode_str(char *to, char *from)
{
    for ( ; *from != '\0'; ++to, ++from)
    {
      if (from == '%' &amp;&amp; isxdigit(from) &amp;&amp; isxdigit(from))
      {

            *to = hexit(from)*16 + hexit(from);

            from += 2;                     
      }
      else
      {
            *to = *from;

      }

    }
    *to = '\0';

}

void listencb(struct evconnlistener* listener, evutil_socket_t fd,
          struct sockaddr* cli, int len, void* ptr){
struct event_base* base = ptr;
struct bufferevent* bev =
    bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);

//set callback
bufferevent_setcb(bev, readcb, writecb, eventcb, NULL);
bufferevent_enable(bev, EV_READ | EV_WRITE);

}

// 断开连接的函数
void disconnect(struct bufferevent* bev){
bufferevent_disable(bev, EV_READ | EV_WRITE);
}

void send_respond_head(struct bufferevent* bev, int no, const char* desp, const char* type, long len){
char buf = {0};
// 状态行
sprintf(buf, "http/1.1 %d %s\r\n", no, desp);
bufferevent_write(bev, buf, strlen(buf));
// 消息报头
memset(buf, 0, sizeof buf);
sprintf(buf, "Content-Type:%s\r\n", type);
sprintf(buf+strlen(buf), "Content-Length:%ld\r\n", len);
bufferevent_write(bev, buf, strlen(buf));
// 空行
bufferevent_write(bev, "\r\n", 2);
}

void send_file(struct bufferevent* bev, const char* path){
int fd = open(path, O_RDONLY);
if(fd == -1){
    //todo 404
    perror("open err");
    exit(1);
}



char buf;
int ret = 0;
while((ret = read(fd, buf, sizeof buf)) &gt; 0){
    bufferevent_write(bev, buf, ret);
}
if(ret == -1){
    perror("read err");
    exit(1);
}
close(fd);
}


void send_dir(struct bufferevent* bev, const char* path){
char file = {0};
char buf = {0};
sprintf(buf, "&lt;html&gt;&lt;head&gt;&lt;title&gt;目录名: %s&lt;/title&gt;&lt;/head&gt;", path);
sprintf(buf+strlen(buf), "&lt;body&gt;&lt;h1&gt;当前目录: %s&lt;/h1&gt;&lt;table&gt;", path);
#if 1
DIR* dir = opendir(path);
if(dir == NULL){
    perror("opendir error");
    exit(1);
}
// 读目录
char enstr = {0};
struct dirent* ptr = NULL;
while((ptr = readdir(dir)) != NULL){
    char* name = ptr-&gt;d_name;
    // 拼接文件的完整路径
    sprintf(file, "%s/%s", path, name);

    encode_str(enstr, sizeof(enstr), name);
    struct stat st;
    int ret = stat(file, &amp;st);
    if(ret == -1){
      perror("stat err");
      exit(1);
    }
    // 如果是文件
    if(S_ISREG(st.st_mode)){
      sprintf(buf+strlen(buf),
              "&lt;tr&gt;&lt;td&gt;&lt;a href=\"%s\"&gt;%s&lt;/a&gt;&lt;/td&gt;&lt;td&gt;%ld&lt;/td&gt;&lt;/tr&gt;",
              enstr, name, (long)st.st_size);
    }
    // 如果是目录
    else if(S_ISDIR(st.st_mode)){
      sprintf(buf+strlen(buf),
              "&lt;tr&gt;&lt;td&gt;&lt;a href=\"%s/\"&gt;%s/&lt;/a&gt;&lt;/td&gt;&lt;td&gt;%ld&lt;/td&gt;&lt;/tr&gt;",
              enstr, name, (long)st.st_size);
    }
    bufferevent_write(bev, buf, strlen(buf));
    memset(buf, 0, sizeof(buf));
}
sprintf(buf+strlen(buf), "&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;");
bufferevent_write(bev, buf, strlen(buf));
closedir(dir);
#endif
}

void http_get_request(const char* line, struct bufferevent* bev){
char method = {0};
char res = {0};
sscanf(line, "%[^ ] %[^ ]", method, res);

// 转码 将不能识别的中文乱码 - &gt; 中文
// 解码 %23 %34 %5f
char destr;
decode_str(destr, res);
// 处理path/xx
// 去掉path中的/
char* file = destr + 1;
char fbuf;
if(strcmp(res, "/") == 0){
    file = "./";
    //getcwd(fbuf, sizeof(fbuf));
    //printf("root:[%s]\n", fbuf);
    //   file = fbuf;
}
   
struct stat st;
printf("file:[%s]\n", file);
int ret = stat(file, &amp;st);
if(ret == -1){
    //todo 404
    perror("stat error");
    exit(1);
}
if(S_ISDIR(st.st_mode)){
    //dir
    // 发送头信息
    send_respond_head(bev, 200, "OK", get_file_type(".html"), 100000);
    send_dir(bev, file);
}
else if(S_ISREG(st.st_mode)){
    //reg file
    send_respond_head(bev, 200, "OK", get_file_type(file), st.st_size);
    send_file(bev, file);
}
}

void readcb(struct bufferevent* bev,void* ctx){
struct evbuffer* evbuf = bufferevent_get_input(bev);

char* line;
size_t len;
line = evbuffer_readln(evbuf, &amp;len, EVBUFFER_EOL_CRLF);
/*
char* tmp = line;
while(1){
    tmp = evbuffer_readln(evbuf, &amp;len, EVBUFFER_EOL_CRLF);
    if(tmp) break;
    printf("%s\n", tmp);
}
printf("line:[%s]", line);
*/
if(strncasecmp("get", line, 3) == 0){
    http_get_request(line, bev);
    //disconnect(bev);
}

}
void writecb (struct bufferevent* bev,void* ctx){

}
void eventcb (struct bufferevent* bev,short what,void* ctx){

}

</code></pre>
<font color="green">
<h1 id="cc-学习互助qq群877684253">c/c++ 学习互助QQ群:877684253</h1>
<p><img src="https://img2018.cnblogs.com/blog/1414315/201811/1414315-20181106214320230-961379709.jpg"></p>
<h1 id="本人微信xiaoshitou5854">本人微信:xiaoshitou5854</h1>
</font><br><br>
来源:https://www.cnblogs.com/xiaoshiwang/p/11159368.html
頁: [1]
查看完整版本: Linux 用libevent实现的简单http服务器