Linux 用epoll实现的简单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 "epoll.h" int main(int argc, char** argv){ if(argc != 3){
printf("eg. port resource_path\n");
exit(1);
}
int port = atoi(argv[1]);
int ret = chdir(argv[2]);
if(ret == -1){
perror("chdir error");
exit(1);
} int lfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY); ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
if(ret == -1){
perror("bind err");
exit(1);
}
listen(lfd, 64); epoll_run(lfd); }

epoll.h

#define EVENT_MAX 1024

void epoll_run(int lfd);
void do_accept(int lfd, int efd);
void do_read(int cfd, int efd);
int get_line(int sock, char *buf, int size);
void http_get_request(const char* line, int cfd);
void send_respond_head(int cfd, int no, const char* desp, const char* type, long len);
const char *get_file_type(const char *name);
void send_file(int cfd, const char* path);
void disconnect(int cfd, int epfd);
void send_dir(int cfd, const char* path);
void encode_str(char* to, int tosize, const char* from);
void decode_str(char *to, char *from);

epoll.c

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include "epoll.h"
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
#include <ctype.h> void epoll_run(int lfd){ int efd = epoll_create(EVENT_MAX);
if(efd == -1){
perror("epoll_create err");
exit(1);
} struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = lfd; int ret = epoll_ctl(efd, EPOLL_CTL_ADD, lfd, &ev);
if(ret == -1){
perror("epoll_ctl err");
exit(1);
} struct epoll_event evs[EVENT_MAX]; while(1){
ret = epoll_wait(efd, evs, EVENT_MAX, -1);
if(ret == -1){
perror("epoll_wait err");
exit(1);
} for(int i = 0; i < ret; ++i){
if(evs[i].data.fd == lfd){
do_accept(lfd, efd);
}
else {
do_read(evs[i].data.fd, efd);
}
}
}
} void do_read(int cfd, int efd){
char line[1024];
int len = get_line(cfd, line, sizeof(line));
printf("head:%s", line);
while(len){
char buf[1024];
len = get_line(cfd, buf, sizeof buf);
printf("%s", buf);
}
if(strncasecmp("get", line, 3) == 0){
http_get_request(line, cfd);
disconnect(cfd, efd);
} } void http_get_request(const char* line, int cfd){
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(cfd, 200, "OK", get_file_type(".html"), 100000);
send_dir(cfd, file);
}
else if(S_ISREG(st.st_mode)){
//reg file
send_respond_head(cfd, 200, "OK", get_file_type(file), st.st_size);
send_file(cfd, file);
}
} void send_dir(int cfd, 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);
}
send(cfd, buf, strlen(buf), 0);
memset(buf, 0, sizeof(buf));
}
sprintf(buf+strlen(buf), "</table></body></html>");
send(cfd, buf, strlen(buf), 0);
#endif
}
// 断开连接的函数
void disconnect(int cfd, int epfd){
int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);
if(ret == -1){
perror("epoll_ctl del cfd error");
exit(1);
}
close(cfd);
} void send_file(int cfd, 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){
send(cfd, buf, ret, 0);
}
if(ret == -1){
perror("read err");
exit(1);
}
close(fd);
}
void send_respond_head(int cfd, 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);
send(cfd, buf, strlen(buf), 0);
// 消息报头
memset(buf, 0, sizeof buf);
sprintf(buf, "Content-Type:%s\r\n", type);
sprintf(buf+strlen(buf), "Content-Length:%ld\r\n", len);
send(cfd, buf, strlen(buf), 0);
// 空行
send(cfd, "\r\n", 2, 0);
}
void do_accept(int lfd, int efd){ struct sockaddr_in cli;
socklen_t len = sizeof(cli);;
int cfd = accept(lfd, (struct sockaddr*)&cli, &len);
if(cfd == -1){
perror("accept err");
exit(1);
} //set nonblock socket
int flag = fcntl(cfd, F_GETFL);
flag |= O_NONBLOCK;
fcntl(cfd, F_SETFL, flag); struct epoll_event ev;
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = cfd;
int ret = epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &ev);
if(ret == -1){
perror("epoll_ctl err");
exit(1);
}
} // 解析http请求消息的每一行内容
int get_line(int sock, char *buf, int size){
int i = 0;
char c = '\0';
int n;
while ((i < size - 1) && (c != '\n')){
n = recv(sock, &c, 1, 0);
if (n > 0){
if (c == '\r'){
n = recv(sock, &c, 1, MSG_PEEK);
if ((n > 0) && (c == '\n')){
recv(sock, &c, 1, 0);
}
else{
c = '\n';
}
}
buf[i] = c;
i++;
}
else{
c = '\n';
}
}
buf[i] = '\0'; return i;
}
// 通过文件名获取文件的类型
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)是&nbsp
*/
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'; }

c/c++ 学习互助QQ群:877684253

本人微信:xiaoshitou5854

Linux 用epoll实现的简单http服务器的更多相关文章

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

    Linux 用libevent实现的简单http服务器 main.c #include <stdio.h> #include <sys/types.h> #include &l ...

  2. 转:Linux下使用Nginx搭建简单图片服务器

    最近经常有人问图片上传怎么做,有哪些方案做比较好,也看到过有关于上传图片的做法,但是都不是最好的,今天再这里简单讲一下Nginx实现上传图片以及图片服务器的大致理念. 如果是个人项目或者企业小项目,仅 ...

  3. 【LINUX/UNIX网络编程】之简单多线程服务器(多人群聊系统)

    RT,Linux下使用c实现的多线程服务器.这个真是简单的不能再简单的了,有写的不好的地方,还希望大神轻拍.(>﹏<) 本学期Linux.unix网络编程的第四个作业. 先上实验要求: [ ...

  4. Linux 下 简单客户端服务器通讯模型(TCP)

    原文:Linux 下 简单客户端服务器通讯模型(TCP) 服务器端:server.c #include<stdio.h> #include<stdlib.h> #include ...

  5. Python——在Python中如何使用Linux的epoll

    在Python中如何使用Linux的epoll 目录 序言 阻塞socket编程示例 异步socket的好处以及Linux epoll 带epoll的异步socket编程示例 性能注意事项 源代码 序 ...

  6. linux下epoll实现机制

    linux下epoll实现机制 原作者:陶辉 链接:http://blog.csdn.net/russell_tao/article/details/7160071 先简单回顾下如何使用C库封装的se ...

  7. linux下epoll如何实现高效处理百万句柄的

    linux下epoll如何实现高效处理百万句柄的 分类: linux 技术分享 2012-01-06 10:29 4447人阅读 评论(5) 收藏 举报 linuxsocketcachestructl ...

  8. 伯克利SocketAPI(一) socket的C语言接口/最简单的服务器和对应的客户端C语言实现

    1. 头文件 2. API函数 3. 最简单的服务器和对应的客户端C语言实现 3.1 server #include <sys/types.h> #include <sys/sock ...

  9. python socket 实现的简单http服务器

    预备知识: 关于http 协议的基础请参考这里. 关于socket 基础函数请参考这里. 关于python 网络编程基础请参考这里. 一.python socket 实现的简单http服务器   废话 ...

随机推荐

  1. (办公)vue下载excel,后台用post方法

    后台方法的参数必须是@RequestBody修饰的. 前台关键代码: axios ( { method : 'post', url : api.exportPlayTime , // 请求地址 dat ...

  2. Jmeter+ant+Jenkins构建接口自动化测试时构建失败 提示:Fatal Error! 字符引用 "&#原因

    Jmeter+ant+Jenkins构建接口自动化测试时构建失败 提示:Fatal Error! 字符引用 "&#原因:接口响应数据中有&#

  3. 爬虫---lxml简单操作

    前几篇写了一些Beautiful Soup的一些简单操作,也拿出来了一些实例进行实践,今天引入一个新的python库lxmt,lxmt也可以完成数据的爬取哦 什么是lxml lxml是python的一 ...

  4. July 13th, 2018. Friday, Week 28th.

    Don't let the mistakes and disappointments of the past control and direct your future. 不要让你的未来被过去的错误 ...

  5. Spring熔断

    Hystrix不再维护,使用Resilience4j来代替.

  6. fiddler设置https抓包

    代理端口设置:127.0.0.1:8888 https抓包设置 模拟post请求: http://xxx.xxxx.com/portal/login.htm Cookie: xxxx account= ...

  7. 洛谷P3723 [AH2017/HNOI2017]礼物

    吴迪说他化学会考上十分钟就想出来了,太神了%%%不过我也十分钟 但是调了一个多小时啊大草 懒得人话翻译了,自己康吧: 我的室友(真的是室友吗?)最近喜欢上了一个可爱的小女生.马上就要到她的生日了,他决 ...

  8. 三大相关系数: pearson, spearman, kendall(python示例实现)

    三大相关系数:pearson, spearman, kendall 统计学中的三大相关性系数:pearson, spearman, kendall,他们反应的都是两个变量之间变化趋势的方向以及程度,其 ...

  9. 剑指offer:二叉搜索树的第k个结点(中序遍历)

    1. 题目描述 /* 给定一棵二叉搜索树,请找出其中的第k小的结点. 例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4. */ 2. 思路 中序遍历二叉搜索树,第K个就 ...

  10. Windows 防火墙无法更改某些设置错误代码 0x80070422

    Windows 防火墙无法更改某些设置错误代码 0x80070422 解决方法: 1.cmd ->services.msc 按下回车键打开服务 :   2.在服务界面双击打开[Windows F ...