http服务器_Epoll类与Server类的设计与实现(C++)
一、回顾Epoll的基础知识
请参见之前的博客文章《UNP学习_I/0复用之epoll函数实现回射服务器》
二、代码讲解
很多的细节我会在注释中讲解。
(1)Epoll Class:
//Epoll.h
#ifndef __EPOLL_H__
#define __EPOLL_H__
#include <vector>
#include <memory>
#include <functional>
#include <sys/epoll.h>
#define MAXEVENTS 4096
namespace huhu {
//前向声明
class HttpRequest;
class ThreadPool;
class Epoll {
public:
using NewConnectionCallback = std::function<void()>;
using CloseConnectionCallback = std::function<void(HttpRequest*)>;
using HandleRequestCallback = std::function<void(HttpRequest*)>;
using HandleResponseCallback = std::function<void(HttpRequest*)>;
Epoll();
~Epoll();
//epoll_ctl系列函数
int addFd(int fd, HttpRequest* request, int events);
int modFd(int fd, HttpRequest* request, int events);
int delFd(int fd, HttpRequest* request, int events);
//epoll_wait
int waitEvents(int time_ms);
//event dispatch 事件分发函数
void handleEvent(int listen_fd, std::shared_ptr<ThreadPool>& thread_pool, int events_num);
//回调设置函数
void setNewConnection(const NewConnectionCallback& cb){
m_new_conn_cb = cb;
}
void setCloseConnection(const CloseConnectionCallback& cb){
m_close_conn_cb = cb;
}
void setRequest(const HandleRequestCallback& cb){
m_req_cb = cb;
}
void setResponse(const HandleResponseCallback& cb){
m_res_cb = cb;
}
private:
using EventList = std::vector<struct epoll_event>;
int m_epoll_fd;
EventList m_eventlist;
NewConnectionCallback m_new_conn_cb;
CloseConnectionCallback m_close_conn_cb;
HandleRequestCallback m_req_cb;
HandleResponseCallback m_res_cb;
};//class epoll
}//namespace huhu
#endif
//Epoll.cpp
#include "../include/Epoll.h"
#include "../include/HttpRequest.h"
#include "../include/ThreadPool.h"
#include <iostream>
#include <cassert>
#include <cstring> // perror
#include <unistd.h> // close
using namespace huhu;
Epoll::Epoll()
//epoll_create1不需要再指定size
: m_epoll_fd(::epoll_create1(EPOLL_CLOEXEC)),
m_eventlist(MAXEVENTS)
{
assert(m_epoll_fd >= 0);
}
Epoll::~Epoll()
{
::close(m_epoll_fd);
}
//向epoll树上添加监听fd的函数
int Epoll::addFd(int fd, HttpRequest* request, int events)
{
struct epoll_event event;
event.data.ptr = static_cast<void*>(request);
event.events = events;
int ret = ::epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, fd, &event);
return ret;
}
//在epoll树上修改监听fd的函数
int Epoll::modFd(int fd, HttpRequest* request, int events)
{
struct epoll_event event;
event.data.ptr = static_cast<void*>(request);
event.events = events;
int ret = ::epoll_ctl(m_epoll_fd, EPOLL_CTL_MOD, fd, &event);
return ret;
}
//向epoll树上删除监听fd的函数
int Epoll::delFd(int fd, HttpRequest* request, int events)
{
struct epoll_event event;
event.data.ptr = static_cast<void*>(request);
event.events = events;
int ret = ::epoll_ctl(m_epoll_fd, EPOLL_CTL_DEL, fd, &event);
return ret;
}
//epoll_wait函数的封装函数
int Epoll::waitEvents(int time_ms)
{
int events_num = ::epoll_wait(m_epoll_fd, &*m_eventlist.begin(), \
static_cast<int>(m_eventlist.size()), time_ms);
if(events_num == 0) {
// printf("[Epoll::wait] nothing happen, epoll timeout\n");
} else if(events_num < 0) {
printf("[Epoll::wait] epoll : %s\n", strerror(errno));
}
return events_num;
}
void Epoll::handleEvent(int listen_fd, std::shared_ptr<ThreadPool>& thread_pool, int events_num)
{
assert(events_num > 0);
for(int i = 0; i < events_num; ++i) {
HttpRequest* request = static_cast<HttpRequest*>(m_eventlist[i].data.ptr);
int fd = request->fd();
if(fd == listen_fd) {
m_new_conn_cb();
} else {
// check error
//如果出错则关闭连接
if((m_eventlist[i].events & EPOLLERR) ||
(m_eventlist[i].events & EPOLLHUP) ||
(!m_eventlist[i].events & EPOLLIN)) {
request->setStopWorking();
m_close_conn_cb(request);
//根据对应的事件向线程池中任务队列添加任务
} else if(m_eventlist[i].events & EPOLLIN) {
request->setWorking();
thread_pool->addTask(std::bind(m_req_cb, request));
} else if(m_eventlist[i].events & EPOLLOUT) {
request->setWorking();
thread_pool->addTask(std::bind(m_res_cb, request));
} else {
printf("[Epoll::handleEvent] unexpected event\n");
}
}
}
return;
}
(2)Server Class:
Reactor模型的基本流程:
//HttpServer.h
#ifndef __HTTP_SERVER_H__
#define __HTTP_SERVER_H__
#include <memory>
#include <mutex>
#define EPOLL_TIMEOUT_MS -1//epoll_wait timeout
#define CONNECT_TIMEOUT 500
#define WORKERS_NUM 4
namespace huhu{
class HttpRequest;
class Epoll;
class ThreadPool;
class TimerManager;
class HttpServer{
public:
HttpServer(int port, int thread_num);
~HttpServer();
void runHuHu();
private:
void __acceptConnection();
void __closeConnection(HttpRequest* request);
void __doRequest(HttpRequest* request);
void __doResponse(HttpRequest* request);
private:
using ListenRequestPtr = std::unique_ptr<HttpRequest>;
using EpollPtr = std::unique_ptr<Epoll>;
using ThreadPoolPtr = std::shared_ptr<ThreadPool>;
using TimerManagerPtr = std::unique_ptr<TimerManager>;
int m_port;
int m_listen_fd;
std::mutex m_mtx;
ListenRequestPtr m_listen_request;
EpollPtr m_epoll;
ThreadPoolPtr m_threadpool;
TimerManagerPtr m_timer_manager;
};//class HttpServer
}//namespace huhu
#endif
//HttpServer.cpp
#include "../include/HttpServer.h"
#include "../include/HttpRequest.h"
#include "../include/HttpResponse.h"
#include "../include/Utils.h"
#include "../include/Epoll.h"
#include "../include/ThreadPool.h"
#include "../include/Timer.h"
#include <iostream>
#include <functional> // bind
#include <cassert> // assert
#include <cstring> // bzero
#include <unistd.h> // close, read
#include <sys/socket.h> // accept
#include <arpa/inet.h> // sockaddr_in
using namespace huhu;
HttpServer::HttpServer(int port, int thread_num)
: m_port(port),
m_listen_fd(utils::createListenFd(m_port)),
m_listen_request(new HttpRequest(m_listen_fd)),
m_epoll(new Epoll()),
m_threadpool(new ThreadPool(thread_num)),
m_timer_manager(new TimerManager())
{
assert(m_listen_fd >= 0);
}
HttpServer::~HttpServer(){}
void HttpServer::runHuHu()
{
//添加监听fd到epoll树上,ET
m_epoll->addFd(m_listen_fd, m_listen_request.get(), (EPOLLIN | EPOLLET));
//设置相应的回调函数
m_epoll->setNewConnection(std::bind(&HttpServer::__acceptConnection, this));
m_epoll->setCloseConnection(std::bind(&HttpServer::__closeConnection, this, \
std::placeholders::_1));
m_epoll->setRequest(std::bind(&HttpServer::__doRequest, this, \
std::placeholders::_1));
m_epoll->setResponse(std::bind(&HttpServer::__doResponse, this, \
std::placeholders::_1));
// event loop
while(1) {
//获取距离下一次超时的时间
int time_ms = m_timer_manager->getNextExpireTime();
int events_num = m_epoll->waitEvents(time_ms);
if(events_num > 0) {
// event dispatch
m_epoll->handleEvent(m_listen_fd, m_threadpool, events_num);
}
//处理超时事件
m_timer_manager->handleExpireTimer();
}
}
// ET
void HttpServer::__acceptConnection()
{
while(1) {
int accept_fd = ::accept4(m_listen_fd, nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC);
if(accept_fd == -1) {
if(errno == EAGAIN)
break;
printf("[HttpServer::__acceptConnection] accept : %s\n", strerror(errno));
break;
}
HttpRequest* request = new HttpRequest(accept_fd);
m_timer_manager->addTimer(request, CONNECT_TIMEOUT, std::bind(&HttpServer::__closeConnection, this, request));
//EPOLLIN, edge trigger, oneshot
m_epoll->addFd(accept_fd, request, (EPOLLIN | EPOLLET | EPOLLONESHOT));
}
}
void HttpServer::__closeConnection(HttpRequest* request)
{
//此处必须加锁,否则在处理较大静态文件响应时会造成2次释放
std::unique_lock<mutex> lock(m_mtx);
int fd = request->fd();
if(request->isWorking()) {
return;
}
m_timer_manager->delTimer(request);
m_epoll->delFd(fd, request, 0);
if(request != nullptr){
delete request;
request = nullptr;
}
}
// LT
void HttpServer::__doRequest(HttpRequest* request)
{
//处理请求时先删除定时器
m_timer_manager->delTimer(request);
assert(request != nullptr);
int fd = request->fd();
int read_error;
int n_read = request->readData(&read_error);
// client disconnection
if(n_read == 0) {
request->setStopWorking();
__closeConnection(request);
return;
}
// connection error
if(n_read < 0 && (read_error != EAGAIN)) {
request->setStopWorking();
__closeConnection(request);
return;
}
// EAGAIN error, m_epoll->mod(...)
if(n_read < 0 && read_error == EAGAIN) {
//因为之前是oneshot,所以这里需要重新modFd,使得下次读写事件就绪的时候epoll_wait会返回
m_epoll->modFd(fd, request, (EPOLLIN | EPOLLONESHOT));
request->setStopWorking();
m_timer_manager->addTimer(request, CONNECT_TIMEOUT, \
std::bind(&HttpServer::__closeConnection, this, request));
return;
}
// parse request
//如果解析请求错误就返回400响应报文,并断开连接
if(!request->parseRequest()) {
HttpResponse response(400, "", false);
request->appendOutBuffer(response.makeResponse());
int write_error;
request->writeData(&write_error);
request->setStopWorking();
__closeConnection(request);
return;
}
//报文解析完成就构造响应报文,modFd为默认的LT模式
if(request->parseFinish()) {
HttpResponse response(200, request->getPath(), request->keepAlive());
request->appendOutBuffer(response.makeResponse());
m_epoll->modFd(fd, request, (EPOLLIN | EPOLLOUT | EPOLLONESHOT));
}
}
// LT
void HttpServer::__doResponse(HttpRequest* request)
{
//处理响应的时候先删除reques的定时器
m_timer_manager->delTimer(request);
assert(request != nullptr);
int fd = request->fd();
int to_write = request->writableBytes();
//如果缓冲区可写大小为0,则设置超时定时器后直接返回
if(to_write == 0) {
m_epoll->modFd(fd, request, (EPOLLIN | EPOLLONESHOT));
request->setStopWorking();
m_timer_manager->addTimer(request, CONNECT_TIMEOUT, \
std::bind(&HttpServer::__closeConnection, this, request));
return;
}
int write_error;
int ret = request->writeData(&write_error);
if(ret < 0 && write_error == EAGAIN) {
m_epoll->modFd(fd, request, (EPOLLIN | EPOLLOUT | EPOLLONESHOT));
return;
}
if(ret < 0 && (write_error != EAGAIN)) {
request->setStopWorking();
__closeConnection(request);
return;
}
//缓冲区的可写空间已经写完
if(ret == to_write) {
if(request->keepAlive()) {//如果是长连接则重新设置解析状态,modFd注册可读事件(因为oneshot),重新设置定时器
request->resetParseStatus();
m_epoll->modFd(fd, request, (EPOLLIN | EPOLLONESHOT));
request->setStopWorking();
m_timer_manager->addTimer(request, CONNECT_TIMEOUT, \
std::bind(&HttpServer::__closeConnection, this, request));
} else {//短连接直接断开连接
request->setStopWorking();
__closeConnection(request);
}
return;
}
//正常情况缓冲区的空间没写完,则modFd读写事件,重新设置定时器
m_epoll->modFd(fd, request, (EPOLLIN | EPOLLOUT | EPOLLONESHOT));
request->setStopWorking();
m_timer_manager->addTimer(request, CONNECT_TIMEOUT, \
std::bind(&HttpServer::__closeConnection, this, request));
return;
}