Linux使用Libevent库实现一个网页服务器---C语言程序

Web服务器

这一个库的实现
其他的知识都是这一个专栏里面的文章

实际使用

编译的时候需要有一个libevent库

gcc httpserv.c -o httpserv -levent

实际使用的时候需要指定端口以及共享的目录

./httpserv 80 .

这一个函数会吧这一个文件夹下面的所有文件共享出去

在这里插入图片描述

实际的效果, 这里我是把我的笔记共享了一下

实现目标

  1. 使用libevent库进行处理客户端连接(listener_cb)
  2. 时候http协议和浏览器进行连接
  3. 获取连接以后把服务启的某个文件夹下面的文件目录返回
  4. 可以根据返回的目录获取文件信息(bufferevent的读回调函数)
  5. 日志会在http.log文件里面
/*************************************************************************> File Name: http0.c> Author: XvSenfeng> Mail: 1458612070@qq.com > Created Time: Thu 18 Apr 2024 11:11:53 AM CST************************************************************************/#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <signal.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>//读取一行
int get_line(struct bufferevent *bev, char *buf, int size){int i = 0;char c = '\0';int n;static char temp, todeal = 0;if(todeal == 1){buf[0] = temp;i++;todeal = 0;}while((i < size - 1) && (c != '\n')){n = bufferevent_read(bev, &c, 1);if(n > 0){if(c == '\r'){n = bufferevent_read(bev, &temp, 1);if((n > 0) && (temp == '\n')){buf[i++] = temp;break;}else{buf[i++] = '\n';todeal = 1;break;}}buf[i] = c;i++;}else{c = '\n';}}buf[i] = '\0';if(n == -1){i = n;}return i;
}
//根据文件的后缀, 获取文件的类型(用于HTTP协议通讯)
//name:文件名
//type:传出参数
void get_file_type(const char *name, char *type){if(strstr(name, ".html")){strcpy(type, "text/html; charset=utf-8");}else if(strstr(name, ".jpg")){strcpy(type, "image/jpeg");}else if(strstr(name, ".png")){strcpy(type, "image/png");}else if(strstr(name, ".gif")){strcpy(type, "image/gif");}else if(strstr(name, ".wav")){strcpy(type, "audio/wav");}else if(strstr(name, ".mp3")){strcpy(type, "audio/mp3");}else if(strstr(name, ".mp4")){strcpy(type, "video/mp4");}else{strcpy(type, "text/plain; charset=utf-8");}
}
//发送一个文件
//filename:文件名
//bev:使用的事件缓冲区
void send_file(const char *filename, struct bufferevent *bev){char buf[1024];//打开文件int fd = open(filename, O_RDONLY);if(fd == -1){perror("open error");exit(1);}//发送文件内容int len = 0;while((len = read(fd, buf, sizeof(buf))) > 0){bufferevent_write(bev, buf, len);}close(fd);
}
//发送HTTP协议的头部
//错误号,错误描述,文件类型,文件长度,bufferevent,文件名
void send_respond(int no, char *disp, char *type, long size, struct bufferevent *bev){//发送http响应char buf[1024] = {0};sprintf(buf, "HTTP/1.1 %d %s\r\n", no, disp);bufferevent_write(bev, buf, strlen(buf));sprintf(buf, "Content-Type: %s\r\n", type);bufferevent_write(bev, buf, strlen(buf));sprintf(buf, "Content-Length: %ld\r\n", size);bufferevent_write(bev, buf, strlen(buf));sprintf(buf, "Connection: close\r\n");bufferevent_write(bev, buf, strlen(buf));sprintf(buf, "\r\n");bufferevent_write(bev, buf, strlen(buf));
}
//把文本转化为URL格式, 可用于网址
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';
}
//发送一个文件夹目录
//dirname:文件夹名字
int send_dir(struct bufferevent *bev,const char *dirname)
{char encoded_name[1024];char path[1024];char timestr[64];struct stat sb;struct dirent **dirinfo;int i;char *buf = malloc(10240);sprintf(buf, "<html><head><meta charset=\"utf-8\"><title>%s</title></head>", dirname);sprintf(buf+strlen(buf), "<body><h1>当前目录:%s</h1><table>", dirname);//添加目录内容int num = scandir(dirname, &dirinfo, NULL, alphasort);for(i=0; i<num; ++i){// 编码strencode(encoded_name, sizeof(encoded_name), dirinfo[i]->d_name);sprintf(path, "%s%s", dirname, dirinfo[i]->d_name);printf("############# path = %s\n", path);if (lstat(path, &sb) < 0){sprintf(buf+strlen(buf), "<tr><td><a href=\"%s\">%s</a></td></tr>\n", encoded_name, dirinfo[i]->d_name);}else{strftime(timestr, sizeof(timestr), "  %d  %b   %Y  %H:%M", localtime(&sb.st_mtime));if(S_ISDIR(sb.st_mode)){//这是一个文件夹sprintf(buf+strlen(buf), "<tr><td><a href=\"%s/\">%s/</a></td><td>%s</td><td>%ld</td></tr>\n",encoded_name, dirinfo[i]->d_name, timestr, sb.st_size);}else{//这是一个普通文件sprintf(buf+strlen(buf), "<tr><td><a href=\"%s\">%s</a></td><td>%s</td><td>%ld</td></tr>\n", encoded_name, dirinfo[i]->d_name, timestr, sb.st_size);}}if(strlen(buf)>10000){break;	}//bufferevent_write(bev, buf, strlen(buf));//    memset(buf, 0, sizeof(buf));}sprintf(buf+strlen(buf), "</table></body></html>");send_respond(200, "OK", "text/html", strlen(buf), bev);bufferevent_write(bev, buf, strlen(buf));printf("################# Dir Read OK !!!!!!!!!!!!!!\n");return 0;
}
//发送一个错误页面
void send_404(struct bufferevent *bev){//发送一个404页面struct stat sbuf;stat("404.html", &sbuf);send_respond(404, "Not Found", "text/html", sbuf.st_size, bev);send_file("404.html", bev);
}
//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;
}void strdecode(char *to, char *from);
void http_request(const char *filename1, struct bufferevent *bev){struct stat sbuf;char filename[1024];strdecode(filename,(char *) filename1);int ret = stat(filename, &sbuf);if(ret != 0){perror("stat error");send_404(bev);return;}char buf[128];get_file_type(filename, buf);//判断是不是目录if(S_ISDIR(sbuf.st_mode)){send_dir(bev, filename);}else{//打开文件//send_respond(200, "OK", "text/html", sbuf.st_size, bev);send_respond(200, "OK", buf, sbuf.st_size, bev);send_file(filename, bev);}	printf("read cb over");
}void read_cb(struct bufferevent *bev, void *arg){char line[1024];int len = get_line(bev, line, sizeof(line));if(len <= 0){printf("get line error\n");bufferevent_free(bev);return;}printf("http header: %s", line);//判断是不是空行if(strcmp(line, "\n") == 0 || strcmp(line, "\r\n") == 0){printf("空行\n");//断开连接bufferevent_free(bev);return;}//判断是不是请求行char path[1024] = {0}, protocol[20] = {0};sscanf(line, "%*s %s %s", path, protocol);//读取剩余数据char buf[1024] = {0};while(1){len = get_line(bev, buf, sizeof(buf));if(len <= 0){break;}if(strcmp(buf, "\n") == 0 || strcmp(buf, "\r\n") == 0){break;}}if(strncasecmp(line, "GET", 3) == 0){char *file = path + 1;if(strcmp(path, "/")==0){file = "./";}http_request(file, bev);signal_over = 1;}else{printf("POST\n");}
}
//写回调, 这一没啥用
void write_cb(struct bufferevent *bev, void *arg){printf("write_cb\n");
}
//事件callback函数, 某一次连接被打断的时候会调用这一个函数
void event_cb(struct bufferevent *bev, short events, void *arg){if(events & BEV_EVENT_EOF){printf("connection closed\n");}else if(events & BEV_EVENT_ERROR){printf("some other error\n");}bufferevent_free(bev);
}//监听的回调函数
void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg){struct event_base *base = (struct event_base *)arg;struct sockaddr_in *sin = (struct sockaddr_in *)addr;//获取客户端ip和端口char ip[16];inet_ntop(AF_INET, &sin->sin_addr.s_addr, ip, sizeof(ip));printf("accept a client %s:%d\n", ip, ntohs(sin->sin_port));//创建bufferevent, 之后使用bufferevent的回调函数处理连接事件struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);if(bev == NULL){printf("bufferevent error");return;}//设置读写回调bufferevent_flush(bev, EV_READ | EV_WRITE, BEV_NORMAL);bufferevent_setcb(bev, read_cb, write_cb, event_cb, arg);bufferevent_enable(bev, EV_READ | EV_WRITE);
}
//处理信号的回调函数
void signal_cb(evutil_socket_t sig, short events, void *user_data)
{struct event_base *base = user_data;struct timeval delay = { 1, 0 };printf("Caught an interrupt signal; exiting cleanly in one seconds.\n");event_base_loopexit(base, &delay);
}
//把一个url
void strdecode(char *to, char *from)
{for ( ; *from != '\0'; ++to, ++from){//检测一下下面的三个字符是不是%xx格式的if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])){//依次判断from中 %20 三个字符, 把这三个字符转换为10进制的数字*to = hexit(from[1])*16 + hexit(from[2]);// 移过已经处理的两个字符(%21指针指向1),表达式3的++from还会再向后移一个字符from += 2;}else{*to = *from;}}*to = '\0';
}int main(int argc, char *argv[]){int pid;//看一下参数的个数对不对if(argc < 3){printf("./serv port path");return 0;}//建立一个守护进程pid = fork();if(pid > 0){exit(1);}//切换工作目录, 使用第三个参数const char *path = argv[2];int ret = chdir(path);if(ret == -1){perror("chdir error");return -1;}pid = setsid();umask(0022);close(STDIN_FILENO);int fd;//一个日志文件fd = open("http.log", O_RDWR|O_CREAT);if(fd==-1){perror("open error");exit(1);}dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);//获取连接的端口int port = atoi(argv[1]);//创建服务器地址struct sockaddr_in serv;//初始化服务器地址memset(&serv, 0, sizeof(serv));serv.sin_family = AF_INET;serv.sin_port = htons(port);serv.sin_addr.s_addr = htonl(INADDR_ANY);//创建event_basestruct event_base *base = event_base_new();if(base == NULL){printf("event base error");return -1;}//创建监听器struct evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1, (struct sockaddr *)&serv, sizeof(serv));if(listener == NULL){printf("listener error");return -1;}struct event *signal_event;//绑定信号回调signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);if (!signal_event || event_add(signal_event, NULL)<0) {fprintf(stderr, "Could not create/add a signal event!\n");return 1;}//开启循环event_base_dispatch(base);evconnlistener_free(listener);event_base_free(base);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/2726.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI大模型重塑新媒体变现格局:智能写作技术助力腾飞!

文章目录 一、AI大模型&#xff1a;新媒体变革的引擎二、智能写作&#xff1a;内容生产的新范式三、精准推送&#xff1a;增强用户粘性的关键四、新媒体变现&#xff1a;插上AI翅膀的飞跃五、挑战与机遇并存&#xff1a;AI与新媒体的未来展望AI智能写作: 巧用AI大模型让新媒体变…

Python-GEE遥感云大数据分析、管理与可视化

原文链接&#xff1a;Python-GEE遥感云大数据分析、管理与可视化https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247601238&idx2&sn6b0557cf61451eaff65f025d648da869&chksmfa820db1cdf584a76de953b96519704177e6206d4ecd47a2f2fabbcac2f7ea619b0bce184…

idm序列号永久激活码2023免费可用 IDM软件破解版下载 最新版Internet Download Manager 网络下载加速必备神器 IDM设置中文

IDM是一款多线程下载工具&#xff0c;全称Internet Download Manager。IDM的多线程加速功能&#xff0c;能够充分利用宽带&#xff0c;所以下载速度会比较快&#xff0c;而且它支持断点续传。它的网站音视频捕获、站点抓取、静默下载等功能&#xff0c;也特别实用。 idm使用技…

数组:最值,反转数组,打乱顺序

文章目录 最值反转数组打乱顺序 位置 最值 package com.zhang.demo; /*这个是求最大值 * * */ public class Test1 {public static void main(String[] args) {int[] arr {13,77,89,333,2,99};int max arr[0];for(int i 1;i < arr.length-1;i){if(max < arr[i]){maxa…

JavaEE 初阶篇-深入了解网络通信相关的基本概念(三次握手建立连接、四次挥手断开连接)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 网络通信概述 1.1 基本的通信架构 2.0 网络通信三要素 3.0 网络通信三要素 - IP 地址 3.1 查询 IP 地址 3.2 IP 地址由谁供应&#xff1f; 3.3 IP 域名 3.4 IP 分…

智慧城市标准化白皮书(2022版)发布

2022年7月25日&#xff0c;国家智慧城市标准化总体组2022年度全体会议召开期间&#xff0c;《智慧城市标准化白皮书&#xff08;2022版&#xff09;》正式发布。 城市作为一个复杂巨系统&#xff0c;是多元主体融合及多元活动集聚的复杂综合体。城市的运行发展关联 到发展、治…

Maven基础篇6

Idea环境中资源上传与下载 具体问题本地仓库如何与私服打交道&#xff1b; 本地仓库向私服上传文件&#xff0c;上传的文件位置在哪里&#xff1f; 访问私服配置相关信息&#xff1a;用户名密码&#xff1b; 下载东西&#xff0c;需要的各种信息&#xff0c;需要的仓库组的…

MES(生产管理系统)开发岗人才定向培养来啦

定向就业培养&#xff0c;职等你来 《中国制造2025》&#xff0c;是我国实施制造强国战略第一个十年的行动纲领&#xff0c;按照“四个全面”战略布局要求&#xff0c;实施制造强国战略&#xff0c;加强 统筹规划和前瞻部署。围绕重点行业转型升级和新一代信息技术、智能制造、…

stripe.js踩坑日记

stripe.js踩坑日记 先附上代码【选择支付方式并唤起对应支付后重定向到支付结果页面】 先安装依赖包 npm install stripe/stripe-js代码【vue3语法】 <template><div class"stripe-pay-ment-box"><div id"payment-element"></div…

【数据库】三、数据库SQL语言命令(基础从入门到入土)

【全文两万多字&#xff0c;涵盖大部分常见情况&#xff0c;建议点赞收藏】 目录 文章目录 目录安装SQL语言1.使用2.DATABASE查看所有库新建数据库修改数据库删除数据库连接数据库 3.TABLE创建表查看库所有表删除表查看表信息重命名表修改表字段&#xff08;列&#xff09;表中…

GUI测试首推!TestComplete 帮助有效缩短 40-50% 测试时长!

TestComplete 是一款自动化UI测试工具&#xff0c;这款工具目前在全球范围内被广泛应用于进行桌面、移动和Web应用的自动化测试。 TestComplete 集成了一种精心设计的自动化引擎&#xff0c;可以自动记录和回放用户的操作&#xff0c;方便用户进行UI&#xff08;用户界面&…

七分钟“手撕”三大特性<多态>

目录 一、学习多态之前需要的知识储备 二、重写 1.什么是重写 2.重写可以干嘛 3.怎么书写重写 4.重载与重写的区别 三、向上转型 1.什么是向上转型&#xff1f; 2.向上转型的语法 3.向上转型的使用场景 四、多态是什么 六、多态实现 七、多态的好处 八、多态的缺…

zookeeper安装原生开发 C API接口时报错

报出的错误&#xff1a;error: %d directive writing between 1 and 5 bytes into a region of size be 问题原因 %d 格式说明符用于格式化有符号十进制整数。它需要一个与要格式化的整数大小相匹配的缓冲区。如果缓冲区太小&#xff0c;则会导致缓冲区溢出&#xff0c;从而可…

码头船只出行及配套货柜码放管理系统-毕设

毕业设计说明书 码头船只出行及配套货柜码放 管理系统 码头船只出行及配套货柜码放管理系统 摘要 伴随着全球化的发展&#xff0c;码头的物流和客运增多&#xff0c;码头业务迎来新的高峰。然而码头业务的增加&#xff0c;导致了人员成本和工作量的增多。为了解决这一基本问题&…

Redis篇:缓存更新策略最佳实践

前景&#xff1a; 缓存更新是redis为了节约内存而设计出来的一个东西&#xff0c;主要是因为内存数据宝贵&#xff0c;当我们向redis插入太多数据&#xff0c;此时就可能会导致缓存中的数据过多&#xff0c;所以redis会对部分数据进行更新&#xff0c;或者把他叫为淘汰更合适&a…

开放式耳机怎样选性价比高?五大性能出色爆款推荐!

在今年的耳机市场&#xff0c;开放式耳机如雨后春笋般涌现&#xff0c;为消费者提供了更多的选择。在这样一个产品繁多的市场中&#xff0c;如何挑选出一款音质上乘、性能卓越的开放式耳机&#xff0c;确实是一个值得探讨的问题。相较于长时间佩戴传统入耳式耳机可能带来的耳朵…

Jenkins 打包报错记录 error: index-pack died of signal 15

问题背景&#xff0c;打包每次到92%时就会报错&#xff0c;试了好几次都是同样的错误 14:56:53 fatal: index-pack failed 14:56:53 14:56:53 at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.launchCommandIn(CliGitAPIImpl.java:2734) 14:56:53 at org.jenkinsci.plugi…

【UE 材质】水波纹效果

效果 模拟雨水打落在水面上的效果 步骤 1. 下载所需纹理和纹理 纹理2. 新建一个材质&#xff0c;这里命名为“M_WaterRipples” 打开“M_WaterRipples”&#xff0c;添加一个纹理采样节点&#xff0c;纹理使用第一步下载的纹理 将纹理采样节点的R通道连接到基础颜色&#x…

MySQL、Oracle查看最大连接数和当前连接数

文章目录 1. MySQL2. Oracle 1. MySQL -- 查看最大连接数 show variables like max_connections; select max_connections; -- select * from performance_schema.session_variables where VARIABLE_NAME in (max_connections); -- select * from performance_schema.global…

产品推荐 | 基于Intel (Altera) Cyclone IV 打造的水星Mercury CA1核心板

01 产品概述 水星Mercury CA1核心板结合了Intel Cyclone IV FPGA、通用接口如USB 2.0和Gigabit Ethernet&#xff0c;具备大量的LVDS I/O、大容量DDR2 SDRAM和大量硬件乘法器&#xff0c;这些使得水星CA1核心板非常适合数字信号处理、网络、高速I/O以及使用Intel NiosII软处理…