接下来正式开始设计。我选择了非阻塞IO,epoll的边缘触发模式。先找了个比较完整的使用epoll的一个socket server例子作为参考,然后在它的基础上边修改边做实验。
这个例子比较简单,而且也没有体现出非阻塞IO编程。不过通过它我了解到了epoll的基本使用方法。
为了实现并发通信,我们需要把程序“摊平”。
首先,分析我们的HTTP服务器通信过程用到的变量:
状态
Wait for reading
Wait for writing
次数
变量类型
非本地变量
备注
Accept
Y
N
n
local
Read request
Y
N
n
nonlocal
Read buf
Open file
N
N
n
nonlocal
文件名
Send response header
N
Y
n
nonlocal
Response header buf
Read file -> Send response content
N
Y
n*n
nonlocal
Read&write buf
Write pos
fd
Sock
读满read buf或读到EOF,再发
发送时将read buf
Close file
N
N
n
fd
Close socket
N
N
n
sock
然后,定义一个结构用于保存这些变量:
struct process {
int sock;
int status;
int response_code;
int fd;
int read_pos;
int write_pos;
int total_length;
char buf[BUF_SIZE];
};
为了简便,我直接用一个全局数组装所有的process:
static struct process processes[MAX_PORCESS];
另外定义每个连接通信过程中的三个状态:
#define STATUS_READ_REQUEST_HEADER 0
#define STATUS_SEND_RESPONSE_HEADER 1
#define STATUS_SEND_RESPONSE
2
之后,就是按部就班地实现主循环、读取request,解析header,判断文件是否存在、检查文件修改时间,发送相应的header和content了。
下面只把程序中跟epoll有关的关键部分贴出来:
main()函数:
使用epoll_create()创建一个epoll fd,注意,这里的listen_sock已经设置为nonblocking(我使用setNonblocking函数)了: