http服务器_Epoll类与Server类的实现(C++)

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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值