0%

WebServer模块单元测试2

前言

接着上一篇博客WebServer模块单元测试 | JySama,写得太多了代码解析就很慢了,重新开一篇。

epoll封装

上一篇末尾给出了epoll的实例,每次添加事件、修改事件、删除事件都是差不多的流程,可以封装一下,并且epollfd的create和close可以变成RAII的管理模式。

因为上层调用时,事件的类型是不一样的(读转读、读转写、写转写等),为了接口易用与通用,让上层传入events描述事件类型。events是一个uinit32_t,也即32位无符号整数类型。

封装很简单,只需要支持添加、修改、删除、调用epoll_wait、设置非阻塞(这个也让上层决定,默认非阻塞)即可。

设置非阻塞

这个功能设置在这里是因为每个要添加进来的事件都可以顺便进行阻塞与非阻塞的设置(而且一般来说事件的模式都是一致的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
阻塞方式是文件读写操作的默认方式,但是应用程序员可通过使用O_NONBLOCK 标志来人为
的设置读写操作为非阻塞方式 .( 该标志定义在 < linux/fcntl.h > 中,在打开文件时指定 ) .

如果设置了 O_NONBLOCK 标志,read 和 write 的行为是不同的 ,如果进程没有数据就绪时调用了 read ,
或者在缓冲区没有空间时调用了 write ,系统只是简单的返回 EAGAIN,而不会阻塞进程.
*/

//fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性
//F_GETFL:获取文件打开方式的标志,标志值含义与open调用一致,然后或上非阻塞标志
//F_SETFL:设置文件打开方式标志为arg指定方式

int setFdNonblock(int fd) {//不判断fd是否小于0,让上层判断
return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);//出错返回-1
}

事件处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bool addFd(int fd, uint32_t events) {
if(fd < 0) return false;
//如果需要设置非阻塞,根据布尔运算就会调用SetFdNonblock函数,如果返回不是-1就成功,是-1就返回false
if(nonBlock && (setFdNonblock(fd)==-1))
return false;

epoll_event ev = {0};
ev.data.fd = fd;//关联fd
ev.events = events;//上层设置好类型
return 0 == epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &ev);//add,成功返回0
}

bool modFd(int fd, uint32_t events) {
if(fd < 0) return false;
epoll_event ev = {0};
ev.data.fd = fd;
ev.events = events;
return 0 == epoll_ctl(epollFd, EPOLL_CTL_MOD, fd, &ev);//mod
}

bool delFd(int fd) {
if(fd < 0) return false;
return 0 == epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, 0);
}

返回就绪事件

这里上层创建一个events数组把地址传进来接收就绪事件,函数返回值是就绪事件个数。虽然c++定义一个结构体类型可以不写struct(c要),但还是写出来好些

1
2
3
4
5
6
7
8
//对于timeout:-1:永远等待;0:不等待直接返回,执行下面的代码;其他:在超时时间内没有事件发生,返回0,如果有事件发生立即返回
//默认不等待
int wait(vector<struct epoll_event> &evlist, int eventSize, int timeoutMs = 0) {//成功返回多少事件就绪,超时返回0,出错返回-1
struct epoll_event* evs = ;
int res = epoll_wait(epollFd, events, eventSize, timeoutMs);
for(int i=0,i<res;i++)
evlist.push_back(events[i]);
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//Epoller.h
#ifndef EPOLLER_H
#define EPOLLER_H

#include <sys/epoll.h> //epoll操作
#include <fcntl.h> // fcntl()
#include <unistd.h> // close()
#include <cassert>
#include <vector>
class Epoller
{
private:
int eventSize;
const bool nonBlock;
int epollFd;

std::vector<struct epoll_event> events;
public:
Epoller(const int eventsize = 1024, const bool ifNonBlock = true):
eventSize(eventsize),nonBlock(ifNonBlock),epollFd(epoll_create(5)),events(eventsize)
{
assert(epollFd>=0);
}
~Epoller()
{
close(epollFd);
}

int setFdNonblock(int fd)
{
return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);//出错返回-1
}

bool addFd(int fd, uint32_t events)
{
if(fd < 0) return false;
//如果需要设置非阻塞,根据布尔运算就会调用SetFdNonblock函数,如果返回不是-1就成功,是-1就返回false
if(nonBlock && (setFdNonblock(fd)==-1))
return false;

epoll_event ev = {0};
ev.data.fd = fd;//关联fd
ev.events = events;//上层设置好类型
return 0 == epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &ev);//add,成功返回0
}

bool modFd(int fd, uint32_t events)
{
if(fd < 0) return false;
epoll_event ev = {0};
ev.data.fd = fd;
ev.events = events;
return 0 == epoll_ctl(epollFd, EPOLL_CTL_MOD, fd, &ev);//mod
}

bool delFd(int fd)
{
if(fd < 0) return false;
return 0 == epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, 0);
}

//对于timeout:-1:永远等待;0:不等待直接返回,执行下面的代码;其他:在超时时间内没有事件发生,返回0,如果有事件发生立即返回
//默认不等待
int wait(int timeoutMs = 0) //成功返回多少事件就绪,超时返回0,出错返回-1
{
return epoll_wait(epollFd, &events[0], eventSize, timeoutMs);
}
int getEventFd(size_t i)
{
return events[i].data.fd;
}
uint32_t getEvents(size_t i)
{
return events[i].events;
}
};

#endif