前面已经介绍过Reactor封装epoll,在前面的基础上实现一个简单的webserver
Reactor关注事件管理的特点使io变得更加灵活,可以实现网络io层与协议层相互独立。也就是说,业务相关处理对网络io影响不大,这就提高了代码复用。
下面具体介绍怎么在Reactor基础上实现webserver
增加业务相关接口
首先我们需要提供两个接口为webserver与客户端(浏览器)之间做数据传输。
int http_request(struct conn *c); // 对应recv
int http_response(struct conn *c); // 对应send
http_request()在io接收数据的接口调用,用于接收数据。
int recv_cb(int fd)
{
int count = recv(fd, conn_list[fd].rbuffer, BUFFER_LEN, 0);
if (count == 0)
{
close(fd);
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
return 0;
}
else if (count < 0)
{
close(fd);
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
return 0;
}
conn_list[fd].rlength = count;
// printf("recv:%s\n", conn_list[fd].rbuffer);
http_request(&conn_list[fd]); // 添加请求接口
set_event(fd, EPOLLOUT, 0);
return count;
}
http_response()在io发送时调用,用于发送数据。
int send_cb(int fd)
{
http_response(&conn_list[fd]); // 增加返回结果接口
int count = 0;
// 状态机检测,下面会讲到
return count;
}
这两个接口本质上是对数据的处理,属于业务层面,对于网络io层,增加了一个状态机用于准备响应头、发送文件内容和完成传输
增加状态机
struct conn
{
int fd;
char rbuffer[BUFFER_LENGTH];
int rlength;
char wbuffer[BUFFER_LENGTH];
int wlength;
RCALLBACK send_callback;
union
{
RCALLBACK recv_callback;
RCALLBACK accept_callback;
} r_action;
int status; // 增加状态机
};
- 0:准备响应头
- 1:发送内容
- 2:发送完成
这个状态机怎么使用呢?
有几个时机需要注意:
在业务接口http_request接收到数据时:初始化对应state=0代表开始准备消息头
printf("request:%s\n", c->rbuffer);
memset(c->wbuffer, 0, BUFFER_LENGTH);
c->wlength = 0;
c->status = 0; // 初始化为0
在业务接口http_response被调用时:
if (c->status == 0)
{
// 准备 HTTP 响应头
c->status = 1;
}
else if (c->status == 1)
{
// 准备文件内容
c->status = 2;
}
else if (c->status == 2)
{
// 完成传输
c->status = 0;
}
当http_response执行完,send_cb会检查状态,这里就可以根据不同的状态执行不同的处理,而不是简单的recv,send之间循环。
if (conn_list[fd].status == 1) // 准备消息头完毕,需要准备内容,继续关注EPOLLOUT
{
count = send(fd, conn_list[fd].wbuffer, conn_list[fd].wlength, 0);
set_event(fd, EPOLLOUT, 0);
}
else if (conn_list[fd].status == 2) // 准备内容完毕,准备结束,继续关注EPOLLOUT
{
set_event(fd, EPOLLOUT, 0);
}
else // conn_list[fd].status == 0 结束,开始关注EPOLLIN
{
if (conn_list[fd].wlength != 0)
{
count = send(fd, conn_list[fd].wbuffer, conn_list[fd].wlength, 0);
}
set_event(fd, EPOLLIN, 0);
}
加入状态机确保了每个连接的状态被正确跟踪,并且在适当的时间点执行相应的操作。
总结
可以看出,对网络io相关的处理改动非常小,这就是Reactor的灵活性带来的好处。网络io层与协议层(业务层)分离,在业务变更时只需要去改动业务相关接口即可,不需要修改网络io层。
4003

被折叠的 条评论
为什么被折叠?



