Linux 用libevent实现的简单http服务器
程序员文章站
2022-12-22 13:49:00
Linux 用libevent实现的简单http服务器 main.c libev.h libev.c c/c++ 学习互助QQ群:877684253 本人微信:xiaoshitou5854 ......
linux 用libevent实现的简单http服务器
main.c
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include "libev.h" #include <string.h> #include <event2/event.h> #include <event2/bufferevent.h> #include <event2/listener.h> int main(int argc, char** argv){ // 端口 int port = atoi(argv[1]); // 修改进程的工作目录, 方便后续操作 int ret = chdir(argv[2]); 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*)&addr, sizeof(addr)); if(!lis){ perror("evconnlistener_new_bind"); exit(1); } event_base_dispatch(base); event_base_free(base); }
libev.h
#ifndef __libev__ #define __libev__ #include <event2/bufferevent.h> #include <event2/listener.h> 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
libev.c
#include "libev.h" #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include "libev.h" #include <event2/event.h> #include <event2/bufferevent.h> #include <event2/buffer.h> #include <string.h> #include <sys/stat.h> #include <stdlib.h> #include <ctype.h> #include <fcntl.h> #include <unistd.h> #include <dirent.h> // 通过文件名获取文件的类型 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 >= '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; } /* * 这里的内容是处理%20之类的东西!是"解码"过程。 * %20 url编码中的‘ ’(space) * %21 '!' %22 '"' %23 '#' %24 '$' * %25 '%' %26 '&' %27 ''' %28 '('...... * 相关知识html中的‘ ’(space)是  */ void encode_str(char* to, int 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'; } void decode_str(char *to, char *from) { for ( ; *from != '\0'; ++to, ++from ) { if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])) { *to = hexit(from[1])*16 + hexit(from[2]); 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[1024] = {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[4096]; int ret = 0; while((ret = read(fd, buf, sizeof buf)) > 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[1024] = {0}; char buf[4096] = {0}; sprintf(buf, "<html><head><title>目录名: %s</title></head>", path); sprintf(buf+strlen(buf), "<body><h1>当前目录: %s</h1><table>", path); #if 1 dir* dir = opendir(path); if(dir == null){ perror("opendir error"); exit(1); } // 读目录 char enstr[1024] = {0}; struct dirent* ptr = null; while((ptr = readdir(dir)) != null){ char* name = ptr->d_name; // 拼接文件的完整路径 sprintf(file, "%s/%s", path, name); encode_str(enstr, sizeof(enstr), name); struct stat st; int ret = stat(file, &st); if(ret == -1){ perror("stat err"); exit(1); } // 如果是文件 if(s_isreg(st.st_mode)){ sprintf(buf+strlen(buf), "<tr><td><a href=\"%s\">%s</a></td><td>%ld</td></tr>", enstr, name, (long)st.st_size); } // 如果是目录 else if(s_isdir(st.st_mode)){ sprintf(buf+strlen(buf), "<tr><td><a href=\"%s/\">%s/</a></td><td>%ld</td></tr>", enstr, name, (long)st.st_size); } bufferevent_write(bev, buf, strlen(buf)); memset(buf, 0, sizeof(buf)); } sprintf(buf+strlen(buf), "</table></body></html>"); bufferevent_write(bev, buf, strlen(buf)); closedir(dir); #endif } void http_get_request(const char* line, struct bufferevent* bev){ char method[12] = {0}; char res[1024] = {0}; sscanf(line, "%[^ ] %[^ ]", method, res); // 转码 将不能识别的中文乱码 - > 中文 // 解码 %23 %34 %5f char destr[1024]; decode_str(destr, res); // 处理path /xx // 去掉path中的/ char* file = destr + 1; char fbuf[1024]; 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, &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, &len, evbuffer_eol_crlf); /* char* tmp = line; while(1){ tmp = evbuffer_readln(evbuf, &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){ }
c/c++ 学习互助qq群:877684253
本人微信:xiaoshitou5854
下一篇: 微服务之间的最佳调用方式