# reactor_http_cpp **Repository Path**: chen_dl/reactor_http_cpp ## Basic Information - **Project Name**: reactor_http_cpp - **Description**: 基于多反应堆的http服务器 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-04 - **Last Updated**: 2025-09-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Reactor Http > 这是一个Linux系统上基于reactor模型设计的一个HTTP服务器,可以解析与响应客户端服务器的请求。 > > 1. 使用了多线程, 并且通过线程池对线程进行了管理 > 2. 使用了多个反应堆模型,且每个线程维护一个反应堆模型 > 1. 每个反应堆模型对应一个任务队列, 任务队列中存储的是通信的文件描述符触发的事件(读, 写, 修改) > 2. 主线程也对应一个反应堆模型, 负责监听, 用来处理客户端的新连接 > 3. 客户端和服务器建立连接之后, 在服务器端会将得到的通信的文件描述符放到子线程对应的某个反应堆模型中进行管理 > 4. 每个反应堆模型拥有一套 IO 多路复用模型 > 5. 每个线程处理的都是对应的那个反应堆模型内部任务队列中的任务, 如果没有任务线程会被IO阻塞, 如果有任务接触IO阻塞并处理任务 # 一、函数主线程的类的包含关系 # ![](readme/项目结构/1.png) > 1、主线程中的TcpServer创建时,会连带着创建一个永远属于主线程的EventLoop,也就是MainLoop,还会创建ThreadPool, > > Listener其实在我的类里面是一个setListen()函数,它不属于对象,它主要是用于得到监听地址的文件描述符。 > > 2、主线程运行时,MainLoop和ThreadPool都会运行,MainLoop只负责监听 listen_fd ,当有客户端连接 listen_fd 时,线程池的某一个子线程会生成一个TcpConnection来处理这个连接 # 二、TaskQueue与ChannelMap的结构 # ![](readme/项目结构/2.png) > 1、Channel是一个注册类,里面包含了监听的文件描述符,读\写事件,读回调函数、写回调函数、释放TcpConnection回调函数,也就是断开连接或出错的情况,回调函数需要的 data > > 2、TaskQueue里面存储的是一个结构体,ChannelElement里面包含了Channel和一个枚举ElemType,ElemType表示调度分发器 > > Dispatcher对Channel中的 fd 做处理(基于epoll、poll、select进行读写事件的添加、删除、修改)。 > > 3、ChannelMap里面存储的是注册好的 fd,在子线程中,监听的客户端如果断开了连接,那么就会删除ChannelMap对应的的Channel,同样,如果有新的连接,那么TcpServer会生成一个TcpConnection,这个TcpConnection也会生成对应的Channel,并且添加进每个Connection对应的EventLoop中 # 三、ThreadPool的结构 # ![](readme/项目结构/3.png) > 1、ThreadPool主要有两种数据成员,一个是属于主线程的EventLoop,还有 N 个WorkerThread,这个MainLoop主要是为了应对这种特殊情况,如果线程池的的线程数量为 0,也就是说没有子线程的情况下,那么所有的处理客户端请求的操作都将在主线程中执行,也就是MainLoop中处理。 > > 2、WorkerThread是工作线程,ThreadID和ThreadName是为了方便调试用的,ThreadMutex和ThreadCondition是用来在并发情况下保护共享数据,子线程中的EventLoop是用来处理客户端文件描述符的读写事件(在TcpConnection中使用) # 四、TcpConnection的结构 # ![](readme/项目结构/4.png) > 1、当客户端发起请求时,才会创建一个TcpConnection,TcpConnection的数量不会超过线程池的线程数量,只有在线程池有空闲的工作线程时,才会创建新的TcpConnection。 > > 2、EventLoop是在线程池运行时被创建,在TcpConnection会引用一个空闲的线程池的一个EventLoop。当有客户端建立新连接时,Channel里面会注册这个客户端的文件描述符,在EventLoop中负责添加任务队列,告诉服务器可以监听这个文件描述符;当客户端断开连接时,也会通知服务器,可以删除这个文件描述符的注册了,并且会释放这个TcpConnection。 > > 3、ReadBuffer和WriteBuffer是读\写缓存,可以临时存储从HttpRequest读到的数据,以便于解析请求;也可以存储HttpResponse组织的响应数据,以便于发送给客户端。 > > 4、HttpRequest是用以解析客户端发来的http请求,HttpResponse是用来响应该请求的 # 五、EventLoop的结构 # ![](readme/项目结构/5.png) > 1、Dispatcher是一个事件分发器,也是一个基类,它的子类是EpollDispatcher、PollDispatcher、SelectDispatcher,它们的底层封装了epoll、poll、select,使用的时候任选其一就行。 > > 2、EventLoop分为主线程的MainLoop,子线程的EventLoop,主线程值负责监听需要服务器的地址,当主线程接收到请求时,会取出一个子线程的EventLoop,并将客户端的文件描述符给子线程,然后子线程会处理这个请求 # 六、程序运行过程 # ![](readme/项目结构/6.png) > 程序运行时,MainReactor会监听服务器的端口与地址,当客户端发起请求时,会触发MainReactor的读回调,会创建一个TcpConnection并注册客户端的文件描述符,在子线程中处理相应的业务逻辑 # 七、启动程序 # > 1. 需要有linux操作系统 > 2. 进入项目的根目录 > 3. 更改common模块的 const.h 中的内容 ```c++ // const.h const std::string WORKSPACE_PATH = std::string("/home/linux/res_test"); // 改为你自己的测试文件路径 const unsigned short PORT = 8000; //改为你自己的虚拟机对外开放的端口 ``` > 3. 执行以下命令 > > ```shell > cd build > rm -rf ./* > cmake .. > make > ../bin/reactor_http_cpp > ``` > 4. 在你自己的浏览器输入自己的服务器地址与端口号 # 八、测试结果 ![image-20250905171147453](readme/项目结构/测试.png)