# 仿mudou的高并发服务器 **Repository Path**: penggli_2_0/TcpServer ## Basic Information - **Project Name**: 仿mudou的高并发服务器 - **Description**: 这是一个仿muduo库One Thread One Loop式主从Reactor模型实现⾼并发服务器项目。 - **Primary Language**: C++ - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 0 - **Created**: 2024-11-12 - **Last Updated**: 2025-04-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 仿mudou的高并发服务器 ## 介绍 这是一个仿muduo库One Thread One Loop式主从Reactor模型实现⾼并发服务器项目。 ## 软件架构 ### TimeWheel 时间轮设计 1. 时间轮模型是解决超时检测的高效策略 可以使用时针轮 分针轮 秒针轮对事件进行监控 该项目中,只使用秒针轮即可满足需求! 2. 时间轮的本质是一个数组 + 一个指针 , 指针没每秒都向后移动 3. 对于同一时间下的任务管理采取二维数组 时间轮的每一个节点都是一个数组,可以储存一系列任务 4. 对于一个连接的非活跃销毁任务,连接建立时 ,就将销毁任务放在30s后 在这30s内如果有了数据通信,就不是一个非活跃连接,所以需要就更新任务。 所以:需要在一个有IO事件产生的时候,延迟定时任务的执行。作为一个时间轮定时器,本身并不关注任务类型! 只要时间到了就执行任务! 5. 对此,时间轮采取 **类的析构函数 + 智能指针shared_ptr**!通过这两个技术可以实习定时任务的触发! 时间轮模型中需要使用一个类,对定时任务进行封装,类实例化的每一个对象就是一个定时任务对象! 当对象被销毁时,再来执行定时任务(析构函数中放入定时任务)。 ### 通用类型any类 每⼀个Connection对连接进⾏管理,**最终都不可避免需要涉及到应⽤层协议的处理**, 因此在Connection中需要设置协议处理的上下⽂来控制处理节奏。但是应⽤层协议千千万,为了降低耦合度,这个协议接收解析上下⽂就不能有明显的协议倾向, 它可以是任意协议的上下⽂信息,**因此就需要⼀个通⽤的类型来保存各种不同的数据结构** 1. 一个连接必须拥有一个请求接收与解析的上下文 2. 上下文的类型或者结构不能固定!因为服务器的协议支持的协议很多 不同的协议,可能都有不同的上下文结构! 所以必须拥有一个容器,能够保存各种不同的类型!那么就要实现一个any类 1. 假如使用模版类方法,那么实例化对象的时候一定要指明容器保存的数据类型! 而我们需要的是any可以接收任意类型`Any a ; a = 10 ; a = "abc"`! 2. 可以嵌套一下,在Any类中设计一个类,专门用于保存各种类型的数据,而Any类保存的是固定类的对象。 3. 对于这个固定类依旧不能使用模版。但这里这里可以采用多态,设计一个子类,这是一个模版类。 这样可以通过父类指针读取子类数据! 4. Any类中,保存的是holder类的指针,当Any类需要保存一个数据时,只需要提供placeholder子类实例化一个 特定类型的对象出来,让子类对象保存数据! ### 缓冲区Buffer类 Buffer模块是⼀个缓冲区模块,⽤于实现通信中⽤⼾态的接收缓冲区和发送缓冲区功能。 1. 需要支持字符串读取/写入 2. 需要支持char*缓冲区读取/写入 3. 需要正常按行读取 --- 方便解析http请求 ### 套接字Socket类 Socket模块是对套接字操作封装的⼀个模块,主要实现的socket的各项操作。 是Connection ,Accpter的基础! ### Reactor反应堆EventLoop模块 EventLoop模块是管理事件监控管理的模块,就是Reactor反应堆模型。 该模块与线程一一对应关联! 监控一个连接,这个连接一旦就绪,就要进行处理! 如果这个连接描述符在多个线程中都触发了事件,就会存在线程安全问题! 因此我们需要将一个连接的事件监控,以及连接事件处理和其他操作都放在同一个线程中进行处理! 后续如果接入了线程池,那么如何保证一个连接的所有操作都在EventLoop所在线程中? 在EventLoop()中,添加一个任务队列 对连接的所有操作,都进行一次封装,对连接的操作当做任务放入任务队列 `事件监控 -> 事件处理(放入队列) -> 执行任务` 这样可以保证对于连接的所有操作都是在一个线程中执行的,不涉及线程安全问题 但是对于任务队列的操作有线程安全问题!只需要给task的操作加一把锁即可! ### 通信连接管理模块Connection类 这是该项目中最重要的一个模块! 该模块就是对连接进行全方位的管理,对通信连接的所有操作都是通过这个模块进行! 需要管理: 1. 套接字的管理,可以进行套接字操作 2. 连接事件的管理,可读,可写,错误,挂断,任意事件 3. 缓冲区的管理,从Socket读取/发送数据 需要经过缓冲区,便于Socket数据的接收与发送 4. 协议上下文的管理,记录请求数据的处理过程 因为连接接收到数据之后要如何处理,需要用户决定,因此必须需要业务处理回调函数! 一个连接建立成功之后,该如何处理,由用户决定!因此必须有连接建立成功的回调函数! 同样关闭前,需要如何处理,也由用户决定,因此必须由关闭连接回调函数。 任意事件的产生,需不需要某种处理,由用户决定,因此必须由任意事件的回调函数! 提供功能: 1. 发送数据 --- 给用户提供的发送数据接口,**只是将数据拷贝到发送缓冲区,然后启动写事件监控** 2. 关闭连接 --- 给用户提供的关闭连接接口,应该在实际释放连接之前,查看输入输出缓冲区是否有数据待处理‘ 3. 超时管理 --- 通过给用户接口,用来 启动/取消 超时销毁功能 4. 协议切换 --- 一个连接接收数据后如何进行业务处理,取决于上下文,以及数据的业务处理函数 Connection模块是对连接的管理模块,对于连接的所有操作都是通过这个模块完成的 场景:对连接进行操作的时候,但是连接已经被释放了,导致内存访问错误,程序崩溃! 对于这个场景的解决方案:使智能指针share_ptr对Connection进行管理,只有计数为0时才会真正释放! ### 获取连接Accept类 专门对监听套接字进行管理的类 1. 创建一个监听套接字 2. 启动读事件监控,获取新连接 3. 事件触发后,获取新连接 4. 为新连接创建Connection进行管理(这是服务器模块进行的) 该模块只进行监听连接的管理,因此获取到新连接的描述符之后,对于新连接描述符如何处理其实并不关心! ### 线程池LoopThreadPool ### 模块整合 TcpServer模块 对所有模块的整合,通过Tcpserver模块实例化的对象,可以非常简单的完成一个服务器的搭建 管理: 1. Acceptor对象:创建一个监听套接字 2. EventLoop对象:BaseLoop对象,实现对监听套接字的事件监控 3. 管理Connection对象的容器:实现对所有连接的管理 4. 管理LoopThreadPool对象:创建loop线程池,对新建连接进行事件监控 功能: 1. 设置从属线程池数量 2. 启动服务器 3. 设置各种回调函数:连接建立完成,消息,关闭,任意。用户设置给TcpServer,TcpServer设置给新连接 4. 是否启动非活跃连接超时销毁功能 5. 添加定时任务功能 流程: 1. 在TcpServer中实例化一个Acceptor对象,以及一个EventLoop对象 2. 将Acceptor挂到baseloop上进行事件监控 3. 一旦Acceptor对象就绪了可读事件,则执行读事件回调函数获取新连接 4. 对新连接创建一个Connection进行管理 5. 对连接对应的Connection设置功能回调(连接完成回调,消息回调,关闭回调,任意回调) 6. 启动Connection的非活跃连接超时规则 7. 将新连接对应的Connection挂到LoopThreadPool的从属线程对应的EventLoop中进行事件监控 8. 一旦新的Connection对应连接就绪了可读事件,这时候执行读事件回调函数,读取数据,之后调用消息回调 ### 实现HttpServer服务器 用于快速实现Http服务器的搭建。 这个模块中有请求方法/资源路径 与 函数指针的映射关系表,可以根据http请求的url找到对应的资源 * 表中记录了对于哪个请求,应该使用哪一个函数来进行业务处理 * 当服务器收到一个请求,就要在请求路由表中,查找是否存在对应的处理函数,没有就返回404 Not Found * 这样做的好处是用户只需要实现业务处理函数,然后将请求与函数的对应关系添加到服务器中,服务器只需要接收数据,解析数据,查找路由表映射关系,执行业务处理函数! 要实现简便的搭建Http服务器,所需的要素和提供的功能 1. GET请求的路由映射表 --- 功能性请求的处理 2. POST请求的路由映射表 3. PUT请求的路由映射表 4. DELETE请求的路由映射表 5. 高性能TCP服务器 --- 进行连接的IO操作 6. 静态资源相对根目录 --- 实现静态资源的处理 服务器处理流程 1. 从Socket接收数据。放到接收缓冲区 2. 调用OnMessage回调函数进行业务处理 3. 对请求进行解析,得到一个HttpRequest结构,包含所有的请求要素 4. 进行请求的路由查找 --- 找到对应请求的处理方法 * 静态资源请求 --- 一些实体文件资源的请求 * 功能性请求 --- 在请求中根据路由映射表查找处理函数 5. 对静态资源请求/功能性请求进行处理完毕后,得到了一个填充了响应信息的HttpReaponse对象,组成http格式报文 接口: 1. 添加请求,处理函数映射信息 2. 设置静态资源根目录 3. 设置线程池数量 4. 启动服务器接口 5. 回调函数OnConnect 用于TcpServer设置协议上下文 回调函数OnMessage 用于进行缓冲区数据解析处理 ## 安装教程 1. xxxx 2. xxxx 3. xxxx ## 使用说明 1. xxxx 2. xxxx 3. xxxx ## 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request