【Linux网络编程】Reactor模式与Proactor模式

news/2024/11/14 14:51:25/文章来源:https://www.cnblogs.com/yangxuanzhi/p/18385524

【Linux网络编程】Reactor模式与Proactor模式

Reactor模式

Reactor 模式是指主线程即 IO 处理单元只负责监听文件描述符上是否有事件发生,有则立刻将该事件通知给工作线程即逻辑单元,除此之外,主线程不做任何其它实质性的动作。读写数据,接受新的连接,以及处理客户请求均在工作线程中完成。

同步 IO 模型实现 Reactor 模式的工作流程:

  • 主线程往 epoll 内核事件表中注册 socket 上的读就绪事件。
  • 主线程调用 epoll_wait 等待 socket 上有数据可读。
  • 当 socket 上有数据可读时,epoll_wait 通知主线程,主线程则将 socket 可读事件放入请求队列。
  • 睡眠在请求队列上的工作线程被唤醒,它从 socket 上读取数据,并处理客户请求,然后往 epoll 内核事件表中注册写就绪事件。
  • 主线程调用 epoll_wait 等待 socket 可写。
  • 当 socket 可写时,epoll_wait 通知主线程,主线程将 socket 可写事件放入请求队列中。
  • 睡眠在请求队列上的某个工作线程被唤醒,它往 socket 上写入服务器处理客户请求的结果。

如下为 Reactor 模式的工作流程图:

image

工作线程从请求队列中取出事件后,将根据事件的类型来决定如何处理它:对于可读事件则执行读数据和处理请求的操作;对于可写事件则执行写数据的操作。因此并没有所谓的“读工作线程”和“写工作线程”。

Proactor 模式

与 Reactor 模式不同,Proactor 模式将所有的 IO 操作交由主线程与内核去处理,工作线程仅仅负责业务逻辑。

异步 IO 模型(以 aio_read 与 aio_write 为例)实现 Proactor 模式的工作流程:

  • 主线程调用 aio_read 函数向内核注册 socket 读完成事件,并告诉内核用户的读缓冲区的位置,以及读操作完成时如何通知应用程序。
  • 主线程继续处理其它逻辑。
  • 当 socket 上的数据被读入用户缓冲区后,内核将向应用程序发送一个信号,以通知应用程序数据已经可用。
  • 应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。工作线程处理完客户请求后,调用 aio_write 函数向内核注册 socket 上的写完成事件,并告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序。
  • 主线程继续处理其它逻辑。
  • 当用户缓冲区中的数据写入 socket 后,内核向应用程序发送一个信号,以通知应用程序数据已经发送完毕。
  • 应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,比如决定是否关闭 socket。

如下为 Proactor 模式的工作流程图:

image

连接 socket 上的读写事件是通过 aio_read / aio_write 向内核注册的,因此内核将通过信号来向应用程序报告连接 socket 上的读写事件。所以,主线程中的 epoll_wait 调用仅用来检测监听 socket 上的连接请求事件,而不能用来检测连接 socket 上的读写事件。

Reactor 模型代码

#include "webserver.h"WebServer::WebServer(int port, int trigMode, int timeoutMs) : port_(port), timeoutMs_(timeoutMs), isClose_(false), epoller_(new Epoller()) {InitEventMode_(trigMode);if(!InitSocket_()) { isClose_ = true };
}WebServer::~WebServer() {close(listenFd_);isClose_ = true;
}void WebServer::Start() {while (!isClose_) {int eventCnt = epoller_->Wait(timeoutMs_);for (int i = 0; i < eventCnt; ++i) {int fd = epoller_->GetEventFd(i);  // 获取事件对应的fduint32_t events = epoller_->GetEvents(i);  // 获取事件的类型if (fd == listenFd_) {DealListen_();} else if (events & EPOLLIN) {DealRead_();  // 子线程中执行} else if (events & EPOLLOUT) {DealWrite_(); // 子线程中执行} else if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {CloseConn_();} else {perror("Unexpected event");}}}
}void WebServer::DealListen_() {struct sockaddr_in cliaddr;socklen_t len = sizeof(cliaddr);do {int cfd = accept(listenFd_, (struct sockaddr*)&cliaddr, &len);if (cfd < 0) { return ; }// 获取客户端信息char cliIp[16];inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, cliIp, sizeof(cliIp));unsigned short cliPort = ntohs(cliaddr.sin_port);// 输出客户端的信息printf("client's ip is %s, and port is %d\n", cliIp, cliPort );SetFdNonblock(cfd);epoller_->AddFd(cfd, connEvent_ | EPOLLIN);} while (listenEvent_ & EPOLLET);
}void WebServer::InitEventMode_(int trigMode) {listenEvent_ = EPOLLRDHUP;connEvent_ = EPOLLRDHUP | EPOLLONESHOT;  // 注册EPOLLONESHOT,防止多线程同时操作一个socket的情况switch ((trigMode)){case 0:break;case 1:connEvent_ |= EPOLLET;break;case 2:listenEvent_ |= EPOLLET;break;case 3:connEvent_ |= EPOLLET;listenEvent_ |= EPOLLET;break;default:connEvent_ |= EPOLLET;listenEvent_ |= EPOLLET;break;}
}bool WebServer::InitSocket_() {// 创建socketlistenFd_= socket(AF_INET, SOCK_STREAM, 0);if (listenFd_ == -1) {perror("socket");return false;}// 设置端口复用int optval = 1;setsockopt(listenFd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));struct sockaddr_in saddr;saddr.sin_family = AF_INET;saddr.sin_addr.s_addr = INADDR_ANY;saddr.sin_port = htons(port_);// 绑定int ret = bind(listenFd_, (struct sockaddr*)&saddr, sizeof(saddr));if (ret == -1) {perror("bind");return false;}// 监听ret = listen(listenFd_, 128);if (ret == -1) {perror("listen");return false;}// 将listenFd_注册至epoll事件表中ret = epoller_->AddFd(listenFd_, listenEvent_ | EPOLLIN);if (ret == 0) {perror("Add listen error");return false;}// 设置listenFd_非阻塞SetFdNonblock(listenFd_);return true;
}int WebServer::SetFdNonblock(int fd) {assert(fd > 0);return fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/788724.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024, 是时候告别CentOS了

到了2024年, 不管你有多喜欢CentOS, 也到了该告别CentOS的时候了. 那个可能在你职业生涯中陪伴了你非常多年, 一直稳定运行的Linux系统, 在2024年后, 已经不再是你可靠的选择了. 最后一个仍然还在维护中的CentOS 7将于2024年6月底就END OF LIFE了. 这意味着, 如果你仍然继续使用…

StringBuilder和StringBuffer的区别

一 区别 StringBuilder 线程不安全 StringBuffer 线程安全,原因是它的主要方法用了syncronized关键字修饰二 分析 看源码见

[Azure Application Insights]Azure应用程序见解概述页面中workspace的link不见了?

问题描述 在Azure Application Insights 的概述页面中,可以直接点击 Workspace Link 进入到 Workspace 资源页面。但是,在下面的示例图中,Workspace Link不见了? 这是什么原因呢? 问题解答 这是因为 Workspace 的资源组发生了改变。 Application Insights 无法根据Worksa…

MYSQL索引的选型比较

MYSQL索引 前言 Mysql 作为互联网中非常热门的数据库,其底层的存储引擎和数据检索引擎的设计非常重要,尤其是 Mysql 数据的存储形式以及索引的设计,决定了 Mysql 整体的数据检索性能。 我们知道,索引的作用是做数据的快速检索,而快速检索的实现的本质是数据结构。通过不同…

Prism:结语

Prism:结语 prism基本功能也已经学完了,在学习过程中遇到过很多问题,其中就有一点条我就一直困惑,那就是:依赖注入 什么是依赖注入:(gpt写的) 依赖注入(Dependency Injection,简称 DI) 是一种设计模式和编程技术,用于实现控制反转(Inversion of Control,IoC)。它…

一个.NET开源、免费的跨平台物联网网关

前言 今天大姚给大家分享一个基于.NET开源、免费的跨平台物联网网关:IoTGateway。 项目介绍 IoTGateway是一个基于.NET6的跨平台物联网网关。通过可视化配置,轻松的连接到你的任何设备和系统(如PLC、扫码枪、CNC、数据库、串口设备、上位机、OPC Server、OPC UA Server、Mqtt…

Prism:事件聚合器

Prism:事件聚合器 Prism框架提供了一个事件聚合器,可以帮助不同模块之间进行解耦和通信。开发人员可以通过发布和订阅事件来实现模块之间的交互。 IEventAggregator松耦合基于事件通讯 多个发布者和订阅者 微弱的事件 过滤事件 传递参数 取消订阅该功能主要作用为, 事件聚合器…

博弈论算法总结

正在完善! 何为博弈论 博弈论 ,是经济学的一个分支,主要研究具有竞争或对抗性质的对象,在一定规则下产生的各种行为。博弈论考虑游戏中的个体的预测行为和实际行为,并研究它们的优化策略。 先来看一道小学就接触过的思维题 你和好基友在玩一个取石子游戏。面前有30颗石子,…

Prism:区域(Region)

Prism:区域(Region) 什么是区域? 区域 (Region) 用于实现模块化应用程序中的视图组织和管理。区域允许您在一个或多个视图容器中动态地加载和卸载视图,从而实现灵活的内容布局和管理。 区域的用途动态内容加载:您可以将不同的视图加载到同一个区域中,这样可以实现在运行时动…

idea import配置

简介 本文记录idea中import相关配置:自动导入依赖、自动删除无用依赖、避免自动导入*包 自动导入依赖在编辑代码时,当只有一个具有匹配名称的可导入声明时,会自动添加导入File -> Settings -> Editor -> General -> Auto Import 勾选Add unambiguous imports on…

【ROS教程】安装ROS全流程及可能遇到的问题

@目录1.配置Softerware & Updates2.添加软件源3.设置key4.更新并安装4.1 更新4.2 安装(ros-noetic-desktop-full)4.2.1 安装aptitude4.2.2 安装ROS软件包5.添加环境变量6.安装构建依赖7.初始化和更新7.1 初始化7.1.1 目前可行的解决办法:重新定位资源7.1.2 结果7.2 更新1.…

table 固定标题的方法(tr标签)

<!DOCTYPE html> <html> <head> <title>带有额外列的表格示例</title> <style> /* 可选的CSS,用于美化表格 */ table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid black; padding: 8px; tex…