用 Linux epoll 实现高性能 HTTP 服务器
为了代码的整洁性,本文章所介绍功能将使用 C++ 实现。实际使用中可转为 C 语言使用。
此项目只能在Linux下使用,windows请绕道。
项目概括
本项目是使用 Linux epoll 实现的一个简单的 HTTP 服务器。仅支持 HTTP 1.0、GET 和 HEAD 方法,对 HTTP 请求报文仅使用正则表达式进行解析。在实际使用中,请使用词法和语法分析来实现请求报文的内容解析。
什么是 epoll
epoll 是 Linux 内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
准备编译环境
本程序需要 gcc,make 相关(make 不是必须)。可通过以下命令安装:
$ sudo apt-get install gcc
$ sudo apt-get install make
项目代码
项目配套代码已上传到 GitHub,项目地址 https://github.com/ZiFung/epoll-http-server/
将项目源码下载下来后,用 terminal 打开,输入
$ make
后输入
$ ./my_http [port]
即可运行
项目成果
本项目经过 GitHub 上的开源项目 wrk 进行压力测试,在测试虚拟机上测得11000并发。
完整代码
项目结构
- epoll_http/
- html/
- index.html
- main.cpp
- HttpServer.hpp
- HttpServer.cpp
- HttpResponse.hpp
- HttpResponse.cpp
- html/
代码
// main.cpp
#include "HttpServer.hpp"
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
// 端口处理
unsigned short port = 80;
if (argc > 3)
{
cerr << "At most two arguments." << endl;
return -1;
}
if (argc == 2)
port = atoi(argv[1]);
else if (argc == 3)
{
if (strcmp(argv[1], "--port") != 0)
{
cerr << "Unknown command \"" << argv[1] << "\"" << endl;
return -2;
}
port = atoi(argv[2]);
}
// 新建服务器
HttpServer *server = new HttpServer();
if (!server->init_server(port))
{
cerr << "Failed in initializing server!" << endl;
return -3;
}
// 启动服务器
server->start_serving();
delete server;
return 0;
}
// HttpServer.hpp
#ifndef HTTPSERVER_HPP
#define HTTPSERVER_HPP
#include "HttpResponse.hpp"
#include <fstream>
#include <sys/epoll.h>
#define MAX_SOCK_SIZE 1024
#define FD_SIZE 1000
#define EPOLLEVENTS_SIZE 1000
#define BUFFER_SIZE 1024 * 1024
using namespace std;
class HttpServer
{
public:
HttpServer();
~HttpServer();
bool init_server(unsigned short port);
void start_serving();
private:
void add_event(int fd, int state);
void modify_event(int fd, int state);
void delete_event(int fd);
private:
int listen_fd = -1;
int epfd = -1;
struct epoll_event events[EPOLLEVENTS_SIZE];
HttpResponse client_data[MAX_SOCK_SIZE];
};
#endif // HTTPSERVER_HPP
// HttpServer.cpp
#