160 Linux C++ 通讯架构实战14,epoll 反应堆模型

到这里,我们需要整理一下之前学习的epoll模型,并根据之前的epoll模型,提出弊端,进而整理epoll反应堆模型,进一步深刻理解,这是因为epoll实在是太重要了。

复习之前的epoll的整体流程以及思路。

参考之前写的epoll的代码

第一步,socket,创建套接字。

int listenfd = Socket(AF_INET, SOCK_STREAM,0);

第二步,setsockopt 设定端口复用

int opt = 1;
Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

第三步, bind函数,将socket和地址结构绑定

Bind(listenfd, (struct sockaddr *)&servaddr,sizeof(servaddr));

第四步,Listen 设置可以同时监听的最大的数量为1024

Listen(listenfd, 1024);

第五步,Epoll_create 创建一个红黑树结点,建议的节点>0

int epfd = Epoll_create(OPEN_MAX);

第六步,Epoll_ctl 先将listenfd添加到 epfd这个红黑树上,EPOLL_CTL_ADD表示是添加节点.EPOLLIN表示监听读事件

    struct epoll_event event;
    bzero(&event, sizeof(event));
    event.events = EPOLLIN; 
    event.data.fd = listenfd;
    //epoll_ct 函数的目的是给这颗红黑树上添加节点,删除节点,修改节点
    Epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd, &event);

第七步,epoll_wait,这时候就需要弄一个循环监听了, 使用 epoll_wait函数等待连接

while (1) {
        //epoll_wait返回值nready为满足监听的总个数。realevents是传出参数,传出满足监听条件的所有的结构体
        nready = epoll_wait(epfd, realevents, OPEN_MAX, -1);

}

第7.1步:

epoll_wait返回后,如果realevents 中的是listenfd,说明有客户端第一次连接过来,需要使用accpet去接受这个链接,生成clientfd ,

int clientfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddrlen);

然后将这个clientfd添加到红黑树上。

                Epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &clientevent);

第7.2步:

epoll_wait返回后,如果realevents 中的是clientfd,那么说明客户端有发真正的数据过来,需要使用read 函数去读取这些数据,完成我们对于数据的处理,然后再使用write发给客户端

realreadnum = Read(socketfd, buf, MAXLINE);

//将读到的数据做处理,我们这里只是小写转大写。
for (int j = 0; j < realreadnum; ++j) {
        buf[j] = toupper(buf[j]);
}
Write(socketfd, buf, realreadnum);

这里会有问题,这是因为在网络环境下,环境是很复杂的,例如:客户端的 滑动窗口 已经满了

fix 方案:epoll 反应堆模型

接上述条件,因此在这里我们需要判断客户端是否能写,如果能写,也就是说:我们需要将 clientfd 的 “写事件” 加入到红黑树上。写完后,将 clientfd 的“写事件” 从红黑树上摘下。

还有一个问题,当我们接受的这些数据还没有处理的时候,我们不希望从 clientfd 再 “读取数据”,因此还需要将 clientfd 的“读事件” 从 红黑树上摘下,等待 给客户端 写数据完成后,再将 读取数据事件  添加回 红黑树上。

整个整理如下:

如果realevents中是clientfd--------> read 数据

-------->将clientfd 从红黑树上摘下(将clientfd 的 读事件通过epoll_del  从 红黑树上摘下),

-------->将clientfd 的写事件 通过epoll_ctl上添加到红黑树上

-------->当epoll_wait的返回时,如果返回中有 写事件 ,再使用write /send 发送数据给客户端

-------->当发送数据完成后,则将clientfd 的写事件从红黑树上摘下

-------->将clientfd 的读事件再次添加到红黑树上,从而形成循环

epoll反应堆模型的再次说明:

在epoll反应堆模型之前,我们需要使用到 epoll_event 中的 data 中的 fd来决定具体是那个连接

struct epoll_event
{uint32_t events;	/* Epoll events */epoll_data_t data;	/* User data variable */
} __EPOLL_PACKED;

typedef union epoll_data
{void *ptr;int fd;uint32_t u32;uint64_t u64;
} epoll_data_t;

在epoll反应堆的时候,我们需要使用到 epoll_event 中的 data 中的 ptr

因为ptr是一个万能指针,可以指向任何东西,因此一般使用的时候,会有一个struct,将相关的信息都放在这个struct中,且这个ptr就指向这个ptr,这样就能保存所有的信息,且会将 epoll_wait成立需要调用的方法,通过回调函数的形式添加到这个struct中,

实际上是多了个参数的使用::   ET + NONBLOCK  轮询 + void *ptr

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/epoll.h>#include "wrap.h"
#define MAXLINE 8192
#define SERV_PORT 8000
#define OPEN_MAX 5000int main() {int ret = 0;//第一步,socket,创建套接字。On  success,  a  file  descriptor  for  the new socket is returnedint listenfd = Socket(AF_INET, SOCK_STREAM,0);//第二步,setsockopt 设定端口复用,代码是固定的,当opt是1的时候,说明可以复用端口。 On success, zero is returned for the standard options.  On error, -1 is returned, and errno is set appropriately.int opt = 1;Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//第三步, bind函数,将socket和地址结构绑定//int Bind(int fd, const struct sockaddr *sa, socklen_t salen)struct sockaddr_in servaddr;bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);Bind(listenfd, (struct sockaddr *)&servaddr,sizeof(servaddr));//第四步,设置可以同时监听的最大的数量为1024,如果改成5000会不会错呢?Listen(listenfd, 1024);//Listen(listenfd, OPEN_MAX);//第五步,创建一个红黑树结点,我们建议这个红黑树的节点为5000int epfd = Epoll_create(OPEN_MAX);//第六步,先将listenfd添加到 epfd这个红黑树上,EPOLL_CTL_ADD表示是添加节点.EPOLLIN表示监听读事件struct epoll_event event;bzero(&event, sizeof(event));event.events = EPOLLIN; event.data.fd = listenfd;//epoll_ct 函数的目的是给这颗红黑树上添加节点,删除节点,修改节点Epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd, &event);//第七步,这时候就需要弄一个循环监听了, 使用 epoll_wait函数等待连接struct epoll_event realevents[OPEN_MAX];int nready = 0;while (1) {//epoll_wait返回值nready为满足监听的总个数。realevents是传出参数,传出满足监听条件的所有的结构体nready = epoll_wait(epfd, realevents, OPEN_MAX, -1);if (nready == -1) {perr_exit("epoll_wait error");}for (int i = 0; i < nready;++i) {if (!(realevents[i].events & EPOLLIN)) {//如果不是"读"事件, 继续循环continue;}int socketfd = realevents[i].data.fd;if (socketfd == listenfd) {//如果是listenfd 的读事件,说明有新的链接过来了,这时候要调用accpetstruct sockaddr_in cliaddr;int cliaddrlen = sizeof(cliaddr);bzero(&cliaddr, cliaddrlen);printf("aaa\n");int clientfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddrlen);char str[INET_ADDRSTRLEN] = {0};//#define INET_ADDRSTRLEN 16printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));//然后将connfd,添加到红黑树上struct epoll_event clientevent;bzero(&clientevent, sizeof(clientevent));clientevent.events = EPOLLIN;clientevent.data.fd = clientfd;Epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &clientevent);}else {//如果不是listenfd,那么就是connectfd了,说明这时候客户端有东西写过来,我们要从客户端读取数据char buf[MAXLINE] = {0};int realreadnum;REREADPOINT:realreadnum = Read(socketfd, buf, MAXLINE);if (realreadnum == 0) {//在网络环境下,read函数返回0,说明是对端关闭了,也就是说,客户端关闭了//那么就应该关闭当前的connect端,并将该监听从 红黑树上 移除printf("read done\n");Epoll_ctl(epfd, EPOLL_CTL_DEL, socketfd, NULL);Close(socketfd);}else if (realreadnum == -1) {if (errno == EINTR) {//说明是被信号打断的,一般要重新readprintf("信号打断\n");goto REREADPOINT;}else if (errno == EAGAIN || errno == EWOULDBLOCK){printf(" WOULDBLOCK \n");//说明在打开文件的时候是使用的O_NONBLOCK方式打开的,但是没有读取到数据//当前代码是不会走到这里的,因为前面代码select的最后一个参数用的NULL,是阻塞的//一般在这里 也要重新读,但是这里有个问题,如果一直都读取不到,会不会死循环?goto REREADPOINT;}else if (errno == ECONNRESET) {//ECONNRESET 说明连接被重置了,因此要将该cfd关闭,并重新移除监听队列printf("read done\n");Epoll_ctl(epfd, EPOLL_CTL_DEL, socketfd, NULL);Close(socketfd);}else {//这就是真正的有问题了,注意这里不要exit程序,应该只是让打印log//不退出程序是因为,这时候还有其他的链接连上的perror("read num <0");}}else if (realreadnum > 0) {//真正的读取到了客户端发送过来的数据for (int j = 0; j < realreadnum; ++j) {buf[j] = toupper(buf[j]);}Write(socketfd, buf, realreadnum);Write(STDOUT_FILENO, buf, realreadnum);}}}}Close(listenfd);Close(epfd);return ret;
}

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

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

相关文章

CSS - 你实现过0.5px的线吗

难度级别:中级及以上 提问概率:75% 我们知道在网页显示或是网页打印中,像素已经是最小单位了,但在很多时候,即便是最小的1像素,精度却不足以呈现所需的线条精度和细节。因此,为了在网页显示和网页打印中呈现更加细致的线条,为了在视觉…

Prometheus+grafana环境搭建Nginx(docker+二进制两种方式安装)(六)

由于所有组件写一篇幅过长&#xff0c;所以每个组件分一篇方便查看&#xff0c;前五篇链接如下 Prometheusgrafana环境搭建方法及流程两种方式(docker和源码包)(一)-CSDN博客 Prometheusgrafana环境搭建rabbitmq(docker二进制两种方式安装)(二)-CSDN博客 Prometheusgrafana环…

自动驾驶涉及相关的技术

当科幻走进现实&#xff0c;当影视照进生活&#xff0c;无数次憧憬的自动驾驶&#xff0c;正在慢慢的梦想成真。小时候天马星空的想象&#xff0c;现在正悄无声息的改变着我们的生活。随着汽车电动化进程的加快&#xff0c;自动驾驶技术映入眼帘&#xff0c;很多人可能感觉遥不…

Jupyter Notebook安装使用(一)

1. 简介 Jupyter Notebook 是一个非常强大的工具&#xff0c;它允许用户创建和共享包含实时代码、方程式、可视化和叙事文本的文档。这种工具特别适合数据清理和转换、数值模拟、统计建模、数据可视化、机器学习等多种应用领域。 2. 安装Jupyter Notebook 2.1. 使用 Anaconda…

Gson的用法

1. 导入依赖 <dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.6</version> </dependency> 2. 使用Gson进行解析 2.1 Gson解析普通对象 package com.jiang.partnetbackend.…

利用Idea实现Ajax登录(maven工程)

一、新建一个maven工程&#xff08;不会建的小伙伴可以参考Idea引入maven工程依赖(保姆级)-CSDN博客&#xff09;&#xff0c;工程目录如图 ​​​​​​​ js文件可以上up网盘提取 链接&#xff1a;https://pan.baidu.com/s/1yOFtiZBWGJY64fa2tM9CYg?pwd5555 提取码&…

Chatgpt掘金之旅—有爱AI商业实战篇|专业博客|(六)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、AI技术创业博客领域有哪些机会&#xff1f; 人工智能&#xff08;AI&#xff09;技术作为当今科技创新的前沿领域&#xff0c;为创业者提供了广阔的机会和挑战。随着AI技…

ABBYY FineReader15免费电脑OCR图片文字识别软件

产品介绍&#xff1a;ABBYY FineReader 15 OCR图片文字识别软件 ABBYY FineReader 15是一款光学字符识别&#xff08;OCR&#xff09;软件&#xff0c;专门设计用于将扫描的文档、图像和照片中的文本转换成可编辑和可搜索的格式。这款软件利用先进的OCR技术&#xff0c;能够识别…

递归算法解读

递归&#xff08;Recursion&#xff09;是计算机科学中的一个重要概念&#xff0c;它指的是一个函数&#xff08;或过程&#xff09;在其定义中直接或间接地调用自身。递归函数通过把问题分解为更小的相似子问题来解决原问题&#xff0c;这些更小的子问题也使用相同的解决方案&…

微信小程序短链接工具推荐

现在微信小程序大行其道&#xff0c;但工作中大部分人选择了短链接的方式来推广微信小程序&#xff0c;那么微信小程序短链接工具哪个好?今天就分享一篇从网上看到的关于《微信小程序短链接工具推荐》文&#xff0c;作者是souki&#xff0c;一起来看看吧! 一、缩链 1、生成方…

Memcached 教程之 PHP 连接 Memcached 服务(十)

PHP 连接 Memcached 服务 在前面章节中我们已经介绍了如何安装 Memcached 服务&#xff0c;接下来我们为大家介绍 PHP 如何使用 Memcached 服务。 PHP Memcache 扩展安装 PHP Memcache 扩展包下载地址&#xff1a;PECL :: Package :: memcache&#xff0c;你可以下载最新稳定…

【VASP学习】在Ubuntu系统安装vasp.5.4.4的全过程(包括VASP官方学习资料、安装过程中相关编辑器的配置、VASP的编译及VASP的测试)

在Ubuntu系统安装vasp.5.4.4的全过程 VASP的简介与相关学习资料安装前的准备工作及说明安装过程intel编译器的安装VASP的编译VASP的测试 参考来源 VASP的简介与相关学习资料 VASP(Vienna Ab initio Simulation Package)是基于第一性原理对原子尺度的材料进行模拟计算的软件。比…

【Linux】线程概念及线程互斥

目录 线程概念 线程优点 线程缺点 线程异常 线程系统编程接口 线程创建及终止 线程等待 使用线程系统接口封装一个小型的C线程库并实现一个抢票逻辑 线程互斥 互斥量的接口 线程互斥实现原理 使用系统加锁接口封装LockGuard 实现自动化加锁 线程安全和可重入函数 …

HTML1:html基础

HTML 冯诺依曼体系结构 运算器 控制器 存储器 输入设备 输出设备 c/s(client客户端) 客户端架构软件 需要安装,更新麻烦,不跨平台 b/s(browser浏览器) 网页架构软件 无需安装,无需更新,可跨平台 浏览器 浏览器内核: 处理浏览器得到的各种资源 网页: 结构 HTML(超…

【Rust】生命周期

Rust 生命周期机制是与所有权机制同等重要的资源管理机制。 之所以引入这个概念主要是应对复杂类型系统中资源管理的问题。 引用是对待复杂类型时必不可少的机制&#xff0c;毕竟复杂类型的数据不能被处理器轻易地复制和计算。 但引用往往导致极其复杂的资源管理问题&#x…

Elasticsearch:我们如何演化处理二进制文档格式

作者&#xff1a;来自 Elastic Sean Story 从二进制文件中提取内容是一个常见的用例。一些 PDF 文件可能非常庞大 — 考虑到几 GB 甚至更多。Elastic 在处理此类文档方面已经取得了长足的进步&#xff0c;今天&#xff0c;我们很高兴地介绍我们的新工具 —— 数据提取服务&…

SCI一区 | Matlab实现NGO-TCN-BiGRU-Attention北方苍鹰算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测

SCI一区 | Matlab实现NGO-TCN-BiGRU-Attention北方苍鹰算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测 目录 SCI一区 | Matlab实现NGO-TCN-BiGRU-Attention北方苍鹰算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测预测效果基本介绍模型…

解决JavaWeb中IDEA2023新版本无法创建Servlet的问题

出现问题&#xff1a;IDEA右键创建Servlet时&#xff0c;找不到选项 原因分析&#xff1a;IDEA的2023版的已经不支持Servlet了&#xff0c;如果还要使用的话&#xff0c;需要自己创建模板使用 创建模板 右击设置&#xff0c;选择&#xff08;File and Code Templates&#x…

Selenium的简单防反爬和浏览器配置

# Selenium的简单使用&#xff1a;https://zhuanlan.zhihu.com/p/557463669 # 防反爬参考&#xff1a;https://blog.csdn.net/weixin_51368459/article/details/125462178 from selenium import webdriver from selenium.webdriver.edge.options import Options# 设置浏览器驱动…