文章目录
前言
本文将从Reactor网络模型封装网络层函数出发,实现一个简单的HTTP服务器。
一、事件驱动Reactor的原理与实现
1.1 基本原理
简单理解:
由io管理转变为对事件进行管理,不同的io事件对应不同的回调函数
- register(注册对应的事件,比如read、write、send…)
- callback(根据不同事件调用不同回调函数)
1.2 Reactor线程模型
根据Reactor的数量和处理资源线程池的数量不同,Reactor模型可以分为三种Reactor线程模型:单Reactor单线程、单Reactor多线程和主从Reactor多线程。
本文将实现最简单的单Reactor单线程模型。
1.3 回调函数封装
对于服务器所用到的几个回调函数进行了封装:
accept、recv、send。
在几个回调函数中有几个不好理解的点:
- 在什么时机调用这些回调
- 当出现读事件时应该调用recv_cb,即在主函数中循环调用
- 在什么时机注册这些回调
- 那么什么时候注册回调呢?在调用accept_cb时注册读事件回调。因为需要epoll先监听到事件
event_register
int event_register(int fd, int event) {
if (fd < 0) return -1;
conn_list[fd].fd = fd;
conn_list[fd].r_action.recv_callback = recv_cb;
conn_list[fd].send_callback = send_cb;
memset(conn_list[fd].rbuffer, 0, BUFFER_LENGTH);
conn_list[fd].rlength = 0;
memset(conn_list[fd].wbuffer, 0, BUFFER_LENGTH);
conn_list[fd].wlength = 0;
set_event(fd, event, 1);
}
int set_event(int fd, int event, int flag) {
if (flag) {
// non-zero add
struct epoll_event ev;
ev.events = event;
ev.data.fd = fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
} else {
// zero mod
struct epoll_event ev;
ev.events = event;
ev.data.fd = fd;
epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
}
}
accept_cb
// listenfd(sockfd) --> EPOLLIN --> accept_cb
int accept_cb(int fd) {
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
int clientfd = accept(fd, (struct sockaddr*)&clientaddr, &len);
//printf("accept finshed: %d\n", clientfd);
if (clientfd < 0) {
printf("accept errno: %d --> %s\n", errno, strerror(errno));
return -1;
}
event_register(clientfd, EPOLLIN); // | EPOLLET
if ((clientfd % 1000) == 0) {