# hps_server
**Repository Path**: hps2002/hps_server
## Basic Information
- **Project Name**: hps_server
- **Description**: 一个简单的http服务器,可以解析http请求,可以推送资源。
- **Primary Language**: C++
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-05-22
- **Last Updated**: 2023-10-23
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 简介
该项目仅用于本人学习,不存在商业用途·
# 执行流程
首先初始化整个项目的前置参数:在 `config.cpp` 中能找到对应的参数,也可以通过 `config.cpp` 调整整个项目的设置。
之后流程:
## WebServer::log_write() 初始化日志模块
日志模块分为同步操作和异步操作。
在日志模块中采用懒汉单例模式。
* 同步:不创建异步线程写日志,直接使用线程池中的线程写日志
* 异步:创建异步线程写日志,通过log的成员函数接口 `Log::flush_log_thread()` , 在该接口中调用 `Log::async_write_log()` 不断地从阻塞队列中取出日志string写入日志文件
## 数据库初始化
在 `WebServer::sql_pool()` 通过数据库连接池类获取数据连接池的指针 `m_connPool`, 调用 `connection_pool::init(...)进行初始化`。
然后调用 `http_conn::initmysql_result(...)` 获取mysql中users表中所有的结果映射在map中。
## 线程池初始化
特判一下线程池中的线程数量是否小于0,以及最大线程的数量是否小于0,如果是的话直接抛出 `std::excepection()`
然后直接创建线程池数组,得到指针 `m_threads` , 并判断线程池是否创建成功
对每个分配的空间创建一条线程,并将每条创建好的线程和主线程进行分离(为了不主线程收子线程的影响而导致阻塞或)
## 选择触发模式
项目中采用的是epoll多路复用所以默认的是LT模式。
## 设置监听socket
`socket(PF_INET, SOCK_STREAM, 0)` 创建socket文件, 得到socket文件描述符
通过 `sockaddr_in` 绑定主机的ip地址以及项目使用的端口号。
通过 `setsockopt(...)` 设置端口可以重用,并使用 `bind(...)` 绑定socket描述符,以及`sockaddr_in` 结构体,但是要转化为 `sockaddr`类型。
然后初始化 `utils` 类,初始化时间单位;
注册epoll内核事件表 `m_epollfd = epoll_create(5)` ,得到epoll事件的文件描述符 `m_epollfd` 。将`epoll_fd`添加到untils类时间链表中。创建一对无名的套接字`m_pipefd`用于进程之间的通信,线程之间使用这一对套接字进行信号的传递。
其中函数`epoll_create(5)`中参数5的作用是告诉内核创建epoll事件表的大小为5.
## 运行
运行采用的是reactor模式,主线程不断接收客户端的连接处理线程,让线程池中的线程进行业务处理最后返回主线程发送响应消息。
使用epoll的多路复用观测事件的发生,
主线程通过在while中不断地轮询事件地发生,并且在`epoll_wait()`中被阻塞,直到有事件发生,返回事件发生的个数以及对应事件的文件描述符。
* 如果是监听的socket文件描述符 `m_listenfd` 说明是新的客户端连接请求。则调用对应的处理函数对新来到的连接调用`accpet()`接收,得到文件描述符 `connfd`,并且注册到timer(定时器中)。
* 如果得到的事件是epoll中的`EPOLLRDHUP`、`EPOLLHUP`、`EPOLLERR`的其中一个,表示服务端关闭了连接,此时调用`deal_timer()`移除对应的定时器。
其中`EPOLLRDHUP`事件是由于服务器接收到对端正常关闭连接的请求触发,`EPOLLHUP`由于对端socket的文件描述符被挂断,服务器自己断开连接; `EPOLLERR` 由于对端的socket文件描述符发生错误,服务器断开连接。
* 当前处理的文件描述符和之前创建的通信套接字`m_pipefd`中读到的一样且事件类型是`EPOLLIN`则进入信号处理函数`dealwithsignal()`:
在 `dealwithsignal()` 函数中通过 `recv()`函数从缓冲区中接收另外先线程的信号存放在`signal[1024]`中,`ret()`返回接收的字节数,如果接收的字节数小于等于0直接返回false;反之则对每个字节的的信号进行处理,如果 `signal[i] == SIGLRM`将time_out设置为true,**(和定时器相关)**;如果 `signal[i] == SIGTERM` 则将stop_server设置为true, 停止服务器。
* 如果 `event[i].events` 是 `EPOLLIN`类型的事件说明要处理客户端上的信号,调用`dealwiththread()`
在`dealwiththread()`函数中:
`sockfd`是要处理客户端socket的文件描述符,在reactor模式下,先调整定时器重置当前客户端socket的计时。如果检测到读事件则调用 `append()` 将改事件放入请求队列中