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.rhkb.cn/news/298569.html

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

相关文章

环信IM集成教程——Web端UIKit快速集成与消息发送

写在前面&#xff1a; 千呼万唤始出来&#xff0c;环信Web端终于出UIKit了&#xff01;&#x1f389;&#x1f389;&#x1f389; 文档地址&#xff1a;https://doc.easemob.com/uikit/chatuikit/web/chatuikit_overview.html 环信单群聊 UIKit 是基于环信即时通讯云 IM SDK 开…

JAVA毕业设计132—基于Java+Springboot+Vue的自习室座位预约小程序管理系统(源代码+数据库)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于JavaSpringbootVue的自习室座位预约小程序管理系统(源代码数据库)132 一、系统介绍 本项目前后端分离带小程序&#xff0c;分为管理员、用户两种角色 1、用户&#xff1a; 注…

echart 仪表盘实现指针的渐变色及添加图片

需求&#xff1a; 在仪表盘中设置指针为渐变色&#xff0c;并在仪表盘中间添加图片。 实现重点&#xff1a; 1、仪表盘指针渐变色的实现 渐变色通过设置pointer的itemStyle属性内的color实现&#xff0c;重点是echart版本&#xff0c;这个原本使用4.8.0的版本不起作用&#xff…

利用nginx-http-flv-module实现三种直播

目录 一、说明 二、目标 三、实现 四、直播地址 一、说明 此文在《流媒体服务器的搭建(支持hls)》《搭建nginx-http-flv-module直播系统》之后编写,很多详细内容需要参考它。 流媒体服务器的搭建(支持hls)

MongoDB 启动异常

Failed to start up WiredTiger under any compatibility version. 解决方案: 删除WiredTiger.lock 和 mongod.lock两个文件&#xff0c;在重新启动。回重新生成新的文件。

Kotlin学习日志(一)TextView、Button、Toast的使用(1)

android:layout_width“wrap_content” android:layout_height“wrap_content”/> import kotlinx.android.synthetic.main.activity_main.* 这句话的意思是引进Kotlin的的控件变量自动映射功能&#xff0c;接下来只要是这个activity_main.xml文件中的控件&#xff0c;我…

汽车网络安全管理

汽车网络安全管理 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c…

SpringBoot快速入门笔记(4)

文章目录 一、Vue框架1、前端环境准备2、简介3、快速开始4、事件绑定 二、Vue组件化开发1、NPM2、Vue Cli3、组件化开发4、SayHello自定义组件5、Movie自定义组件 一、Vue框架 1、前端环境准备 编码工具&#xff1a;VSCode 依赖管理&#xff1a;NPM 项目构建&#xff1a;VueCl…

(ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类

文章目录 相关论文摘要引言类别嵌入局限性——问题1普通ZSL模型局限性——问题2自动属性注释过程——对应问题1深度语义-视觉对齐&#xff08;DSVA&#xff09;模型——对应问题2 基于遥感多模态相似性的自动属性标注属性词汇表构造使用CLIP模型自动标注属性对CLIP模型进行训练…

【Arthas案例】某应用依赖两个GAV-classifier不同的snakeyaml.jar,引起NoSuchMethodError

多个不同的GAV-classifier依赖冲突&#xff0c;引起NoSuchMethodError Maven依赖的三坐标体系GAV(G-groupId&#xff0c;A-artifactId&#xff0c;V-version) classifier通常用于区分从同一POM构建的具有不同内容的构件物&#xff08;artifact&#xff09;。它是可选的&#xf…

51之矩阵键盘

目录 1.矩阵键盘简介 2.获取矩阵键盘键码值 3.矩阵键盘实现密码锁 1.矩阵键盘简介 矩阵键盘就是一个基于独立按键的Plus版本&#xff0c;它的原理图就是下面这样&#xff1a; 和独立按键就像表兄弟一样&#xff0c;为什么这么说呢&#xff1f;因为这个矩阵键盘上可以找到很多…

java——文件上传

一、文件上传——简介 文件上传的简介&#xff1a;文件上传是指将本地计算机中的文件传输到网络上的服务器或另一台计算机上的过程。在 Web 开发中&#xff0c;文件上传通常指的是将用户通过 Web 页面提交的文件&#xff08;如图像、文档、音频、视频等&#xff09;传输到服务器…

大数据实验统计-1、Hadoop安装及使用;2、HDFS编程实践;3、HBase编程实践;4、MapReduce编程实践

大数据实验统计 1、Hadoop安装及使用&#xff1b; 一&#xff0e;实验内容 Hadoop安装使用&#xff1a; 1&#xff09;在PC机上以伪分布式模式安装Hadoop&#xff1b; 2&#xff09;访问Web界面查看Hadoop信息。 二&#xff0e;实验目的 1、熟悉Hadoop的安装流程。 2、…

【Rust】生命周期

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

归并排序解读

在算法领域中&#xff0c;排序算法一直是一个核心话题。归并排序&#xff08;Merge Sort&#xff09;作为一种典型的分治思想应用&#xff0c;以其稳定、高效的特点受到了广泛的关注和应用。本文将深入探讨归并排序的原理、实现方式&#xff0c;以及它在实际应用中的价值。 一…

KeyguardClockSwitch的父类

KeyguardClockSwitch 定义在KeyguardStatusView中, mClockView findViewById(R.id.keyguard_clock_container);KeyguardClockSwitch的父类为&#xff1a; Class Name: LinearLayout Class Name: KeyguardStatusView Class Name: NotificationPanelView Class Name: Notificat…

如何在iPhone上恢复永久删除的照片?

2007 年&#xff0c;Apple Inc. 推出了这款震撼人心的智能手机&#xff0c;后来被称为 iPhone。您会惊讶地发现&#xff0c;迄今为止&#xff0c;Apple Inc. 已售罄 7 亿台 iPhone 设备。根据 2023 年 8 月的一项调查数据&#xff0c;95% 的智能手机利润都落入了苹果公司的口袋…

关系型数据库与非关系型数据库、Redis数据库

相比于其他的内存/缓存数据库&#xff0c;redis可以方便的实现持久化的功能&#xff08;保存至磁盘中&#xff09; 一、关系数据库与非关系型数据库 1.1 关系型数据库 一个结构化的数据库&#xff0c;创建在关系模型基础上一般面向于记录 SQL语句 (标准数据查询语言) 就是一种…

【攻防世界】ics-05

php://filter 伪协议查看源码 preg_replace 函数漏洞 1.获取网页源代码。多点点界面&#xff0c;发现点云平台设备维护中心时&#xff0c;页面发生变化。 /?pageindex 输入什么显示什么&#xff0c;有回显。 用php://filter读取网页源代码 ?pagephp://filter/readconvert.…

MPLS-基础、LSR、LSP、标签、体系结构

MPLS技术 MPLS基础 MPLS&#xff1a;转发数据时&#xff0c;只在网络边缘分析IP报文头&#xff0c;不在每一跳都分析&#xff0c;节约了转发时间。 MPLS&#xff1a;Multiprotocol Label Switching&#xff0c;多协议标签交换骨干网技术。主要应用&#xff1a;VPN、流量工程…