使用Epoll进行实现的简单Web服务器
本项目的并行性通过线程池(Posix线程)以及epoll进行实现
-
主线程执行初始化工作(配置网络、监听端口等);
-
主线程创建epoll实例,将需要监听的文件描述符添加到epoll的兴趣列表中;
-
主线程创建
THREAD_NUM个线程(epollfd以及listenfd会被作为参数传递),随后主线程和子线程都将作为工作线程存在; -
所有工作线程在epoll实例上调用
epoll_wait函数; -
当任何事件被捕捉到时,工作线程会执行下列操作:
-
检查
fd是否为listenfd,如果是,那么对其调用accept并将connfd(accept的返回值)添加到兴趣列表中; -
如果不是,那么
fd就是connfd,此时可以从中读取数据。如果读取完成(检测到新的一行),那么响应此请求。否则将读取状态保存到event.data.ptr中读取状态的定义如下:
typedef struct HttpStatus { int connfd; // 连接文件描述符 char *header; // 已读取的HTTP请求头 size_t readn; // 已读取的字节数 FILE *file; // 需要发送的文件 size_t left; // 尚需发送的文件 req_status_t req_status; } http_status_t;
req_status_t被定义为:typedef enum REQUEST_STATUS { Reading, Writing, Ended } req_status_t;
下一次
epoll_wait接收到此fd中的事件时,服务器会继续处理此请求 -
请求头读取完毕之后,
connfd会进入Writing状态。如果sendfile函数发送数据时触发了EAGAIN事件,同时left > 0,那么意味着写端暂时不可用。保存当前状态到HttpStatus中之后,利用EPOLL_CTL_MOD将connfd以EPOLLOUT | EPOLLET重新装配到epoll实例中;
-
