Qver - 用于练手的服务器程序
利用 C++ 11 编写一个简单的 Linux HTTP 服务器,主要用于静态页面的部署
项目地址:xQmQ/Qver: C++ 11 实现的简单 HTTP server
httpEvent(int fd):推入到线程池任务队列的处理函数,通过fd创建HttpEvent类型对象,调用相应的处理函数,完成对 HTTP 请求的处理
HttpEvent:HTTP 请求处理类
ThreadPool:线程池实现,通过ThreadPool::submitWork()推送任务并唤醒工作线程处理
Socket:注册并绑定 socket 连接到服务器特定端口,接受所有客户连接
Epoller:epoll的封装实现,用于接收socket接受的连接并监听其是否有读写请求;通过定时器剔除非活动连接
Timer:定时器,用于处理非活动连接

Socket对象,建立ThreadPool对象Socket对象源源不断接收连接并注册到Epoller监听,并插入Timer定时器Epoller监听到就绪读请求时,获取对应fd并通过httpEvent(int fd)注册到线程池任务队列,由线程池唤醒工作线程并处理;同时从Timer中剔除链接Timer定期处理非活动连接,从Epoller中剔除并关闭httpEvent(int fd)HttpEvent对象,绑定fdHttpEvent获取fd中的数据到读缓冲区,通过有限状态机解析 HTTP 请求,并准备相应资源写入写缓冲区,并发送给客户流程:
epoll_wait()获得客户端的读写请求,将读写请求插入任务队列缺点:
流程:
详情:《Linux 多线程服务端编程》9.1.2 小节
epoll_wait()获得客户端的读写请求empty()和size()都设置为const,与函数中上锁的操作发生了冲突ThreadPool中定义了一个返回类型推导的任务提交函数submitWork(),类中的函数声明与定义分离在Threadpool.h和ThreadPool.cpp中,但是模板函数的声明和定义不可以分离。我的解决办法是将submitWork()定义在Threadpool.h中ThreadPool可能存在线程不安全的情况。在构造函数中,创建工作线程并向工作线程暴露了ThreadPool的this指针,如果ThreadPool初始化到一半,其他线程会访问这个半成品;析构函数中也存在问题,在代码中存在唤醒工作线程执行余下的任务这样的操作,这些工作线程会调用ThreadPool::conditional_mutex_,如果析构函数销毁了互斥锁,对于未结束的工作线程来说会破坏互斥环境《Linux 多线程服务端编程》1.2,1.3
shutdown()来定义,不应该放在析构函数中,何时执行shutdown()?考虑到这是一个服务器,不用关闭,需要关闭的时候应当由外部信号来决定是否调用shutdown()addEvent()时对检测的事件调整为EPOLLET | EPOLLIN | EPOLLRDHUP;且处理EPOLLIN之前需要先close()对端关闭的 socketEPOLLRDHUP和EPOLLIN | EPOLLOUT的处理需要注意:应当优先处理EPOLLRDHUP,然后再处理其他操作EPOLLOUT的触发问题。高位表示缓冲区可写,低位表示不可写。在水平触发LT中,由低位转高位和处于高位这两种情况都会触发EPOLLOUT;在边缘触发ET中,只有低位转高位才会触发EPOLLOUT。一般来说,在连接建立时,缓冲区是可写的,这时就会触发,所以处于ET时,后续操作中会存在无法触发EPOLLOUT导致无法执行写操作,可以通过epoll_ctl()中EPOLL_CTL_MOD重新设置一次EPOLLOUTstruct sigaction.sa_handler,其记录的是信号触发时的处理函数,以回调函数的形式表示。且此处理函数只能有一个参数,为信号值。在原本的设计中,计划将这些涉及到信号处理的函数封装成类,但是如果封装成类,处理函数作为成员函数时,无法作为回调函数绑定在struct sigaction.sa_handler(此时有两个参数,一个是隐藏参数this)Qver - 用于练手的服务器程序
利用 C++ 11 编写一个简单的 Linux HTTP 服务器,主要用于静态页面的部署
项目地址:xQmQ/Qver: C++ 11 实现的简单 HTTP server
httpEvent(int fd):推入到线程池任务队列的处理函数,通过fd创建HttpEvent类型对象,调用相应的处理函数,完成对 HTTP 请求的处理
HttpEvent:HTTP 请求处理类
ThreadPool:线程池实现,通过ThreadPool::submitWork()推送任务并唤醒工作线程处理
Socket:注册并绑定 socket 连接到服务器特定端口,接受所有客户连接
Epoller:epoll的封装实现,用于接收socket接受的连接并监听其是否有读写请求;通过定时器剔除非活动连接
Timer:定时器,用于处理非活动连接

Socket对象,建立ThreadPool对象Socket对象源源不断接收连接并注册到Epoller监听,并插入Timer定时器Epoller监听到就绪读请求时,获取对应fd并通过httpEvent(int fd)注册到线程池任务队列,由线程池唤醒工作线程并处理;同时从Timer中剔除链接Timer定期处理非活动连接,从Epoller中剔除并关闭httpEvent(int fd)HttpEvent对象,绑定fdHttpEvent获取fd中的数据到读缓冲区,通过有限状态机解析 HTTP 请求,并准备相应资源写入写缓冲区,并发送给客户流程:
epoll_wait()获得客户端的读写请求,将读写请求插入任务队列缺点:
流程:
详情:《Linux 多线程服务端编程》9.1.2 小节
epoll_wait()获得客户端的读写请求empty()和size()都设置为const,与函数中上锁的操作发生了冲突ThreadPool中定义了一个返回类型推导的任务提交函数submitWork(),类中的函数声明与定义分离在Threadpool.h和ThreadPool.cpp中,但是模板函数的声明和定义不可以分离。我的解决办法是将submitWork()定义在Threadpool.h中ThreadPool可能存在线程不安全的情况。在构造函数中,创建工作线程并向工作线程暴露了ThreadPool的this指针,如果ThreadPool初始化到一半,其他线程会访问这个半成品;析构函数中也存在问题,在代码中存在唤醒工作线程执行余下的任务这样的操作,这些工作线程会调用ThreadPool::conditional_mutex_,如果析构函数销毁了互斥锁,对于未结束的工作线程来说会破坏互斥环境《Linux 多线程服务端编程》1.2,1.3
shutdown()来定义,不应该放在析构函数中,何时执行shutdown()?考虑到这是一个服务器,不用关闭,需要关闭的时候应当由外部信号来决定是否调用shutdown()addEvent()时对检测的事件调整为EPOLLET | EPOLLIN | EPOLLRDHUP;且处理EPOLLIN之前需要先close()对端关闭的 socketEPOLLRDHUP和EPOLLIN | EPOLLOUT的处理需要注意:应当优先处理EPOLLRDHUP,然后再处理其他操作EPOLLOUT的触发问题。高位表示缓冲区可写,低位表示不可写。在水平触发LT中,由低位转高位和处于高位这两种情况都会触发EPOLLOUT;在边缘触发ET中,只有低位转高位才会触发EPOLLOUT。一般来说,在连接建立时,缓冲区是可写的,这时就会触发,所以处于ET时,后续操作中会存在无法触发EPOLLOUT导致无法执行写操作,可以通过epoll_ctl()中EPOLL_CTL_MOD重新设置一次EPOLLOUTstruct sigaction.sa_handler,其记录的是信号触发时的处理函数,以回调函数的形式表示。且此处理函数只能有一个参数,为信号值。在原本的设计中,计划将这些涉及到信号处理的函数封装成类,但是如果封装成类,处理函数作为成员函数时,无法作为回调函数绑定在struct sigaction.sa_handler(此时有两个参数,一个是隐藏参数this)