一个网站的后台,怎么设计网站内容,好用的磁力搜索引擎,家庭电脑可以做网站吗1、什么是Reactor模型 Reactor意思是“反应堆”#xff0c;是一种事件驱动机制。 和普通函数调用的不同之处在于#xff1a;应用程序不是主动的调用某个 API 完成处理#xff0c;而是恰恰相反#xff0c;Reactor逆置了事件处理流程#xff0c;应用程序需要提供相应的接口并…1、什么是Reactor模型 Reactor意思是“反应堆”是一种事件驱动机制。 和普通函数调用的不同之处在于应用程序不是主动的调用某个 API 完成处理而是恰恰相反Reactor逆置了事件处理流程应用程序需要提供相应的接口并注册到 Reactor 上如果相应的时间发生Reactor将主动调用应用程序注册的接口这些接口又称为“回调函数”。 对于刚开始接触这个机制个人感觉翻译成“感应器”可能会更好理解一点因为注册在Reactor上的函数就像感应器一样只要有事件到达就会触发它开始工作。 Reactor 模式是编写高性能网络服务器的必备技术之一。 2、Reactor模型的优点
响应快不必为单个同步时间所阻塞虽然 Reactor 本身依然是同步的编程相对简单可以最大程度的避免复杂的多线程及同步问题并且避免了多线程/进程的切换开销可扩展性强可以方便的通过增加 Reactor 实例个数来充分利用 CPU 资源可复用性高reactor 框架本身与具体事件处理逻辑无关具有很高的复用性 Reactor 模型开发效率上比起直接使用 IO 复用要高它通常是单线程的设计目标是希望单线程使用一颗 CPU 的全部资源。 优点即每个事件处理中很多时候可以不考虑共享资源的互斥访问。可是缺点也是明显的现在的硬件发展已经不再遵循摩尔定律CPU 的频率受制于材料的限制不再有大的提升而改为是从核数的增加上提升能力当程序需要使用多核资源时Reactor 模型就会悲剧 , 为什么呢 如果程序业务很简单例如只是简单的访问一些提供了并发访问的服务就可以直接开启多个反应堆每个反应堆对应一颗 CPU 核心这些反应堆上跑的请求互不相关这是完全可以利用多核的。例如 Nginx 这样的 http 静态服务器。 3、通过对网络编程epoll代码的优化深入理解Reactor模型
1、epoll的普通版本根据fd类型listen_fd和client_fd分为两大类处理。 如果是listen_fd调用accept处理连接请求 如果是client_fd调用recv或者send处理数据。 代码实现 #include stdio.h
#include string.h
#include stdlib.h#include unistd.h
#include netinet/tcp.h
#include arpa/inet.h
#include sys/epoll.h#include errno.hint main(int argc, char* argv[])
{if (argc 2)return -1;int port atoi(argv[1]); //字符串转换为整型int sockfd socket(AF_INET, SOCK_STREAM, 0);if (sockfd 0)return -1;struct sockaddr_in addr;memset(addr, 0, sizeof(struct sockaddr_in)); //新申请的空间一定要置零addr.sin_family AF_INET;addr.sin_port htons(port); //转换成网络字节序addr.sin_addr.s_addr INADDR_ANY;if (bind(sockfd, (struct sockaddr*)addr, sizeof(struct sockaddr_in)) 0)return -2;if (listen(sockfd, 5) 0)return -3;//epollint epfd epoll_create(1); //创建epoll,相当于红黑树的根节点struct epoll_event ev, events[1024] {0}; //events相当于就绪队列一次性可以处理的集合ev.events EPOLLIN;ev.data.fd sockfd;epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, ev); //将ev节点加入到epoll此处的sockfd参数随便添加没有意义需要操作系统索引和它有对应的句柄while (1){int nready epoll_wait(epfd, events, 1024, -1); //第四个参数-1表示一直等待有事件才返回if (nready 1) //没有事件触发nready代表触发事件的个数break;int i 0;for (i 0; i nready; i) //epoll_wait带出的就绪fd包括两大类1、处理连接的listen_fd2、处理数据的send和recv{if (events[i].data.fd sockfd) //如果是listenfd就将它加入到epoll{struct sockaddr_in client_addr;memset(client_addr, 0, sizeof(struct sockaddr_in));socklen_t client_len sizeof(client_addr);int client_fd accept(sockfd, (struct sockaddr *)client_addr, client_len);if (client_fd 0)continue;char str[INET_ADDRSTRLEN] {0};printf(recv from IP %s ,at Port %d\n, inet_ntop(AF_INET, client_addr.sin_addr, str, sizeof(str)), ntohs(client_addr.sin_port));ev.events EPOLLIN | EPOLLET; //epoll默认是LT模式ev.data.fd client_fd;epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, ev);}else //fd进行读写操作{//对fd的读写操作没有分开int client_fd events[i].data.fd;char buf[1024] {0};int ret recv(client_fd, buf, 1024, 0);if (ret 0){if (errno EAGAIN || errno EWOULDBLOCK){//}else{//}printf(ret 0,断开连接%d\n, client_fd);close(client_fd);ev.events EPOLLIN;ev.data.fd client_fd;epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, ev);}else if (ret 0) //接收到了客户端发来的断开连接请求FIN后没有及时调用close函数进入了CLOSE _WAIT状态{printf(ret 0,断开连接%d\n, client_fd);close(client_fd);ev.events EPOLLIN;ev.data.fd client_fd;epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, ev); //close关闭连接后要将它既是从epoll中删除}else{printf(Recv: %s, %d Bytes\n, buf, ret);}//区分fd的读写操作即recv和sendif (events[i].events EPOLLIN){int client_fd events[i].data.fd;char buf[1024] {0};int ret recv(client_fd, buf, 1024, 0);if (ret 0){if (errno EAGAIN || errno EWOULDBLOCK){//...}else{//...}printf(ret 0,断开连接%d\n, client_fd);close(client_fd);ev.events EPOLLIN;ev.data.fd client_fd;epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, ev);}else if (ret 0) //接收到了客户端发来的断开连接请求FIN后没有及时调用close函数进入了CLOSE _WAIT状态{printf(ret 0,断开连接%d\n, client_fd);close(client_fd);ev.events EPOLLIN;ev.data.fd client_fd;epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, ev); //close关闭连接后要将它既是从epoll中删除}else{printf(Recv: %s, %d Bytes\n, buf, ret);}}if (events[i].events EPOLLOUT) //为什么需要判断EPOLLOUT而不是直接else因为一个fd有可能同时存在可读和可写事件的{int client_fd events[i].data.fd;char buf[1024] {0};send(client_fd, buf, sizeof(buf), 0);}}}}return 0;
} 2、epoll的优化版本根据事件类型读和写分为两大类处理。 代码实现 for (i 0; i nready; i) //epoll_wait带出的就绪fd包括两大类1、处理连接的listen_fd2、处理数据的send和recv{//区分fd的读写操作if (events[i].events EPOLLIN){if (events[i].data.fd sockfd) //如果是listenfd就将它加入到epoll{struct sockaddr_in client_addr;memset(client_addr, 0, sizeof(struct sockaddr_in));socklen_t client_len sizeof(client_addr);int client_fd accept(sockfd, (struct sockaddr *)client_addr, client_len);if (client_fd 0)continue;char str[INET_ADDRSTRLEN] {0};printf(recv from IP %s ,at Port %d\n, inet_ntop(AF_INET, client_addr.sin_addr, str, sizeof(str)), ntohs(client_addr.sin_port));ev.events EPOLLIN | EPOLLET; //epoll默认是LT模式ev.data.fd client_fd;epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, ev);}else {int client_fd events[i].data.fd;char buf[1024] {0};int ret recv(client_fd, buf, 1024, 0);if (ret 0){if (errno EAGAIN || errno EWOULDBLOCK){//...}else{//...}printf(ret 0,断开连接%d\n, client_fd);close(client_fd);ev.events EPOLLIN;ev.data.fd client_fd;epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, ev);}else if (ret 0) //接收到了客户端发来的断开连接请求FIN后没有及时调用close函数进入了CLOSE _WAIT状态{printf(ret 0,断开连接%d\n, client_fd);close(client_fd);ev.events EPOLLIN;ev.data.fd client_fd;epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, ev); //close关闭连接后要将它既是从epoll中删除}else{printf(Recv: %s, %d Bytes\n, buf, ret);}}}//为什么需要判断EPOLLOUT而不是直接else因为一个fd有可能同时存在可读和可写事件的if (events[i].events EPOLLOUT) {int client_fd events[i].data.fd;char buf[1024] {0};send(client_fd, buf, sizeof(buf), 0);}} 3、epoll的Reactor模式 epoll由以前的对网络iofd进行管理转变成对events事件进行管理。 代码实现
#include stdio.h
#include string.h
#include stdlib.h#include unistd.h
#include netinet/tcp.h
#include arpa/inet.h
#include sys/epoll.h#include errno.h//每个fd所对应的信息
struct sockitem
{int sockfd;int (*callback)(int fd, int events, void*arg);char sendbuf[1024];char recvbuf[1024];
};//每个epoll所对应的信息
struct epollitem
{int epfd;struct epoll_event events[1024]; //events相当于就绪队列一次性可以处理的集合
};struct epollitem *eventloop NULL;int recv_cb(int fd, int events, void*arg);
int send_cb(int fd, int events, void*arg);int accept_cb(int fd, int events, void*arg)
{printf(---accept_cb(int fd, int events, void*arg)---\n);struct sockaddr_in client_addr;memset(client_addr, 0, sizeof(struct sockaddr_in));socklen_t client_len sizeof(client_addr);int client_fd accept(fd, (struct sockaddr *)client_addr, client_len);if (client_fd 0)return -1;char str[INET_ADDRSTRLEN] {0};printf(recv from IP %s ,at Port %d\n, inet_ntop(AF_INET, client_addr.sin_addr, str, sizeof(str)), ntohs(client_addr.sin_port));struct epoll_event ev;ev.events EPOLLIN | EPOLLET; //epoll默认是LT模式struct sockitem *si (struct sockitem*)malloc(sizeof(struct sockitem));si-sockfd client_fd;si-callback recv_cb;ev.data.ptr si;epoll_ctl(eventloop-epfd, EPOLL_CTL_ADD, client_fd, ev);return client_fd;
}int recv_cb(int fd, int events, void*arg)
{printf(---recv_cb(int fd, int events, void*arg)---\n);struct epoll_event ev;struct sockitem *sit (struct sockitem*)arg;int ret recv(fd, sit-recvbuf, 1024, 0);if (ret 0){if (errno EAGAIN || errno EWOULDBLOCK){//...}else{//...}printf(ret 0,断开连接%d\n, fd);ev.events EPOLLIN;epoll_ctl(eventloop-epfd, EPOLL_CTL_DEL, fd, ev); //close关闭连接后要将它既是从epoll中删除close(fd);free(sit); //连接关闭后释放内存}else if (ret 0) //接收到了客户端发来的断开连接请求FIN后没有及时调用close函数进入了CLOSE _WAIT状态{printf(ret 0,断开连接%d\n, fd);ev.events EPOLLIN;epoll_ctl(eventloop-epfd, EPOLL_CTL_DEL, fd, ev); close(fd);free(sit);}else{printf(Recv from recvbuf: %s, %d Bytes\n, sit-recvbuf, ret);ev.events EPOLLIN | EPOLLOUT; //sit-sockfd fd;sit-callback send_cb;ev.data.ptr sit;epoll_ctl(eventloop-epfd, EPOLL_CTL_MOD, fd, ev);}return ret;
}int send_cb(int fd, int events, void*arg)
{struct epoll_event ev;struct sockitem *sit (struct sockitem*)arg;strncpy(sit-sendbuf, sit-recvbuf, sizeof(sit-recvbuf) 1);send(fd, sit-sendbuf, sizeof(sit-recvbuf) 1, 0);ev.events EPOLLIN | EPOLLET; //sit-sockfd fd;sit-callback recv_cb;ev.data.ptr sit;epoll_ctl(eventloop-epfd, EPOLL_CTL_MOD, fd, ev);return fd;
}int main(int argc, char* argv[])
{if (argc 2)return -1;int port atoi(argv[1]); //字符串转换为整型int sockfd socket(AF_INET, SOCK_STREAM, 0);if (sockfd 0)return -1;struct sockaddr_in addr;memset(addr, 0, sizeof(struct sockaddr_in)); //新申请的空间一定要置零addr.sin_family AF_INET;addr.sin_port htons(port); //转换成网络字节序addr.sin_addr.s_addr INADDR_ANY;if (bind(sockfd, (struct sockaddr*)addr, sizeof(struct sockaddr_in)) 0)return -2;if (listen(sockfd, 5) 0)return -3;//epolleventloop (struct epollitem *)malloc(sizeof(struct epollitem));eventloop-epfd epoll_create(1); //创建epoll,相当于红黑树的根节点struct epoll_event ev;ev.events EPOLLIN | EPOLLET;struct sockitem *si (struct sockitem*)malloc(sizeof(struct sockitem));si-sockfd sockfd;si-callback accept_cb;ev.data.ptr si; //将fd和对应的回调函数绑定一起带进epollepoll_ctl(eventloop-epfd, EPOLL_CTL_ADD, sockfd, ev); //将ev节点加入到epoll此处的sockfd参数随便添加没有意义需要操作系统索引和它有对应的句柄while (1){int nready epoll_wait(eventloop-epfd, eventloop-events, 1024, -1); //第四个参数-1表示一直等待有事件才返回if (nready 1) //没有事件触发nready代表触发事件的个数break;int i 0;for (i 0; i nready; i){//区分fd的读写操作if (eventloop-events[i].events EPOLLIN){struct sockitem *sit (struct sockitem*)eventloop-events[i].data.ptr;sit-callback(sit-sockfd, eventloop-events[i].events, sit); //不用区分listen_fd和recv_fd,相应的fd都会调用他们所对应的callback}//为什么需要判断EPOLLOUT而不是直接else因为一个fd有可能同时存在可读和可写事件的if (eventloop-events[i].events EPOLLOUT) {struct sockitem *sit (struct sockitem*)eventloop-events[i].data.ptr;sit-callback(sit-sockfd, eventloop-events[i].events, sit);}}}return 0;
} 4、Reactor模型的应用 1、单线程模式的Reactor参考libevent、redis 2、多线程模式的Reactor参考memcached 3、多进程模式的Reactor参考nginx。