epoll接口

B站就业班视频代码搬运 p54

但是我跟老师的代码还是有点区别。。老师那里居然ev复用。。那么数组里那些结构体都用不上??

注意,本篇不是epoll反应堆。

I/O多路复用一共有select , poll ,epoll等模型,但是真正的高并发的话是epoll。原因:selcet 和poll都是把监控的文件描述符从应用程序拷贝到内核,然后挨个询问再把有事件发生的拷贝到程序内存,总之消耗很大,最好不好超过1024。所以掌握epoll更好。

当epoll采用默认的水平触发模式的时候,可以认为是一个更快速的Poll .

epoll有简单版本和复杂版本,复杂的叫做epoll反应堆模型,需要你自定义一个结构体,其中的内容你自己写,但是必须至少包括 1个文件描述符、1个回调函数、1个参数。哈哈这部分我还没看懂,所以今天搬运一个简单的epoll服务器代码啦

详细内容参考这位博主的文章

详细参考

1.基本函数介绍

1.1 创建epoll红黑树

 int epoll_create(int size);

参数:必须填写一个大于0的数。

返回值 

   成功: 一个非零的 文件描述符,是红黑树的 树根节点。
    失败:-1 并设置errno
 

1.2 添加监控的文件描述符(上树、修改、删除)

 int epoll_ctl  (int epfd,  int op,   int fd,   struct epoll_event *event);

epfd就是上面那个create 函数的返回值。

op的值:

 EPOLL_CTL_ADD
              将一个文件描述符添加到epoll树上

 EPOLL_CTL_MOD
         改变树上某个文件描述符的一些属性设定。

 EPOLL_CTL_DEL
            将某个制定的文件描述符从列表中删除,此时第四个参数可以写NULL 但是注意有BUG (自己man   epoll_ctl 自己看一下BUG吧我没看)

fd:  你要处理的文件描述符。

struct epoll_event  *event :肯定是这个名叫event的地址啦!那么event的数据结构是啥?它是一种结构体,名字叫做 epoll_event .

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

 这个void*万能指针在后面的 epoll反应堆模型里有很大用处,当然这里没啥用 这里用fd就行了

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

1.3 回收

 int epoll_wait(int epfd,    struct epoll_event *events,    int maxevents,   int timeout);

epfd: 红黑树的树根

 struct epoll_event  *event :这里不是单个event的地址了 而是数组的首地址,这个数组每个成员都是结构体epoll_event

maxevents: 一般是1024 反正必须大于0

timeout: 你自己规定的超时时间,单位是毫秒

-1  永远不算超时,一直等待、阻塞

0  立即返回

返回值:

成功: 在  I/O读写中,已经就绪的文件描述符的个数, 如果一个都没有就返回0

失败:  -1 and errno is set appropriately.
 

2.使用epoll的高并发服务器

客户端的代码,前面一篇文章去拷贝就行了

//epoll模型
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <sys/epoll.h>int main() {int sfd = socket(AF_INET, SOCK_STREAM, 0); //设置端口复用int opt =1;setsockopt ( sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));//定义服务器地址的 结构体,利用本机任意ip,端口为8888struct sockaddr_in servad;bzero(&servad, sizeof(servad));servad.sin_family =AF_INET;servad.sin_port =htons(8888);servad.sin_addr.s_addr = htonl (INADDR_ANY);int ret = bind(sfd, (struct sockaddr*)&servad, sizeof( servad));//监听描述符sfd 设置完成listen(sfd,128);  //创建一颗 epoll红黑树int epfd = epoll_create(1024); if (epfd<0){perror ("epoll create fail\n");return -1;} //将监听描述符添加到树上struct epoll_event sev;sev.events = EPOLLIN;sev.data.fd = sfd;epoll_ctl (epfd, EPOLL_CTL_ADD,sfd,&sev);struct epoll_event evarray[1024];int nready =0; while (1) {//超时时间设置-1,所以进程会无限阻塞在这一步,除非有了变化才往下走nready = epoll_wait (epfd,evarray,1024,-1);if (nready < 0) {if (errno == EINTR){continue;}break;}for (int i =0;i<nready;i++){int sockfd =evarray[i].data.fd;//第一种情况,有客户端的新请求if (sockfd == sfd) {int newfd = accept (sfd,NULL, NULL);//将新的通信描述符newfd上树evarray[i].events =EPOLLIN;evarray[i].data.fd = newfd;epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &(evarray[i]));continue;}//第二种情况,眼前的描述符,有可读事件发生 char buf[256];//读数据memset(buf,0x00,sizeof(buf));int n =read (sockfd, buf, sizeof(buf));//int n = recv (sockfd, buf, sizeof(buf), 0);if (n <=0){ printf("这里是服务器端, read error or client close\n"); close (sockfd);//sockfd 下树这里第四个参数可是NULLepoll_ctl (epfd, EPOLL_CTL_DEL, sockfd, NULL);continue;}else {printf ("这里是服务器端n =%d ,读到的是%s \n", n,buf);//将小写字母都转化wie大写for (int j =0; j<n; j++) { buf[j] =toupper (buf[j]); }    //发送数据write (sockfd, buf, n);}}}
close (sfd);
close (epfd);
return 0;
}

3. 如何测试你的epoll是ET (边缘触发) 还是LT (水平触发):

默认都是水平触发,只要缓冲区有字符,就一直读,写100个,读限制为30字符,那就读3次。如何判断出来的呢?

3.1 判断epoll是LT的代码: 

把上面 n =read ( XXX 最后一个参数改成2 ) 让它每次只能读俩字符。然后你再客户端中,敲10个字符。这时你会发现,服务器分6次向你返回了所有字符的大写(最后又一个空回车)

说明默认是LT模式

3.2 ET 模式怎么设置

XXXX.events  = EPOLLIN | EPOLLET;   /*边沿触发 */ 

read函数 最后一个参数改成2 ,再把通信描述符设置成ET模式,

//将新的通信描述符    newfd上树
             evarray[i].events =EPOLLIN | EPOLLET;

你发送10个字符之后,服务器只返回俩字符;你再敲回车,又返回俩字符;直到读完为止。

3.3 ET模式下,就想一口气读完所有数据

while 1 加上去看似很简单 

//现在是一口气读完所有数据ETmoshi 
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <sys/epoll.h>int main() {int sfd = socket(AF_INET, SOCK_STREAM, 0); //设置端口复用int opt =1;setsockopt ( sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));//定义服务器地址的 结构体,利用本机任意ip,端口为8888struct sockaddr_in servad;bzero(&servad, sizeof(servad));servad.sin_family =AF_INET;servad.sin_port =htons(8888);servad.sin_addr.s_addr = htonl (INADDR_ANY);int ret = bind(sfd, (struct sockaddr*)&servad, sizeof( servad));//监听描述符sfd 设置完成listen(sfd,128);  //创建一颗 epoll红黑树int epfd = epoll_create(1024); if (epfd<0){perror ("epoll create fail\n");return -1;} //将监听描述符添加到树上struct epoll_event sev;sev.events = EPOLLIN;sev.data.fd = sfd;epoll_ctl (epfd, EPOLL_CTL_ADD,sfd,&sev);struct epoll_event evarray[1024];int nready =0; while (1) {//超时时间设置-1,所以进程会无限阻塞在这一步,除非有了变化才往下走nready = epoll_wait (epfd,evarray,1024,-1);if (nready < 0) {if (errno == EINTR){continue;}break;}for (int i =0;i<nready;i++){int sockfd =evarray[i].data.fd;//第一种情况,有客户端的新请求if (sockfd == sfd) {int newfd = accept (sfd,NULL, NULL);//将新的通信描述符newfd上树evarray[i].events =EPOLLIN | EPOLLET;evarray[i].data.fd = newfd;epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &(evarray[i]));continue;}//第二种情况,眼前的描述符,有可读事件发生 char buf[256];while (1) {//读数据memset(buf,0x00,sizeof(buf));int n =read (sockfd, buf, 2);//int n = recv (sockfd, buf, sizeof(buf), 0);if (n <=0){ printf("这里是服务器端, read error or client close\n"); close (sockfd);epoll_ctl (epfd, EPOLL_CTL_DEL, sockfd, NULL);break;}else {printf ("这里是服务器端n =%d ,读到的是%s \n", n,buf);//将小写字母都转化wie大写for (int j =0; j<n; j++) { buf[j] =toupper (buf[j]); }    //发送数据write (sockfd, buf, n);}}}}
close (sfd);
close (epfd);
return 0;
}

但是此时会出现一个BUG 第一个客户端连接了以后,其他客户端的连接,服务器似乎不管了。。只有眼前这一个退出了才行。。

原因:read函数返回值小于0的时候,你关闭了文件描述符。。就算你不写关闭文件描述符的代码,read 或者recv函数一直在那里等待着。 

3.3 如何解决新问题( ET模式) 

将传递信息的文件描述符(通信描述符)设置为非阻塞(未完待续)

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

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

相关文章

优先级队列(堆)详解

优先级队列&#xff08;堆&#xff09;详解 目录 堆的概念堆的存储方式堆的基本操作优先级队列模拟实现PriorityQueue接口介绍堆排序Top-k问题 1、堆的概念 如果有一个关键码的集合K {k0&#xff0c;k1&#xff0c; k2&#xff0c;…&#xff0c;kn-1}&#xff0c;把它的所…

安卓ADB实操教程:以三星Galaxy S10为例

引言 ADB&#xff08;Android Debug Bridge&#xff09;是一个功能强大的命令行工具&#xff0c;它允许用户与安卓设备进行通信以便进行各种操作&#xff0c;包括安装和调试应用程序&#xff0c;访问设备的shell等。本教程将以三星Galaxy S10为例&#xff0c;详细介绍如何使用…

SAP 五个报废率设置简介(上)

通常在生产制造过程中都会面临报废率的问题,生产工艺路线的问题,原材料质量的问题,总会有一些产品在生产过程中被做成报废品,通常报废率的设置有时候会遵循行业的标准设置,亦或者根据工厂生产中统计的历史数据分析后根据不同的产品设置不同的报废率,从而在执行物料的采购…

VM下Unbunt虚拟机上网设置

系列文章目录 VM下虚拟机上网设置 VM下虚拟机上网设置 右击VM软件中你需要设置的虚拟机&#xff0c;选择设置 宿主机如果你用的是笔记本外加WIFI连接选择NAT网络模式 进入虚拟机看能否上网 不行的话&#xff0c;进入虚拟机点击&#xff0c;选择最后一栏&#xff0c;编辑连接 点…

华为认证的HCIP考实验考试么?

HCIP在考试的时候不考实验&#xff0c;只考笔试&#xff0c;只是不同方向的HCIP认证考试的考试科目不同&#xff0c;有的考一科&#xff0c;有的考二科&#xff0c;有的考三科&#xff0c;具体看方向来定。HCIA和HCIP只考笔试。HCIE考笔试和实验。 虽然HCIP不考实操&#xff0…

《WebKit 技术内幕》学习之七(1): 渲染基础

《WebKit 技术内幕》之七&#xff08;1&#xff09;&#xff1a; 渲染基础 WebKit的布局计算使用 RenderObject 树并保存计算结果到 RenderObject 树。 RenderObject 树同其他树&#xff08;如 RenderLayer 树等&#xff09;&#xff0c;构成了 WebKit 渲染的为要基础设施。 1…

【数据结构】链表(单链表与双链表实现+原理+源码)

博主介绍&#xff1a;✌全网粉丝喜爱、前后端领域优质创作者、本质互联网精神、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战✌有需要可以联系作者我哦&#xff01; &#x1f345;附上相关C语言版源码讲解&#x1f345; &#x1f44…

python04-变量命名规则

python需要使用标识符来给变量命名。 标识符&#xff0c;我来解释下&#xff0c;就是给程序中变量、类、方法命名的符号&#xff0c;简单理解就是起一个名字&#xff0c;这个名字必须是合法的名字&#xff0c; 对于Python来说&#xff0c;标识符必须是以字母、下划线(_)开头&…

鸿蒙自定义刷新组件使用

前言 DevEco Studio版本&#xff1a;4.0.0.600 1、RefreshLibrary_HarmonyOS.har&#xff0c;用于HarmonyOS "minAPIVersion": 9, "targetAPIVersion": 9, "apiReleaseType": "Release", "compileSdkVersion": "3.…

用户画像系列——在线服务调优实践

前面文章讲到画像的应用的几个方面&#xff0c;其中画像的在线服务应用主要是在推荐场景、策略引擎场景&#xff0c;这两部分场景都是面向线上的c端服务。 推荐场景&#xff1a;根据不同的用户推荐不同的内容&#xff0c;做到个性化推荐&#xff0c;需要读取画像的一些偏好数据…

openssl3.2/test/certs - 008 - root-nonca trust variants: +serverAuth +anyEKU

文章目录 openssl3.2/test/certs - 008 - root-nonca trust variants: serverAuth anyEKU概述笔记END openssl3.2/test/certs - 008 - root-nonca trust variants: serverAuth anyEKU 概述 openssl3.2 - 官方demo学习 - test - certs 笔记 // \file my_openssl_win_log_doc…

20240123----重返学习-原生js纯函数获取用户电脑上的文件

20240123----重返学习-原生js纯函数获取用户电脑上的文件 思路说明 通过外加点击后&#xff0c;通过监听这个DOM元素的change事件&#xff0c;在用户点击之后就能拿到用户电脑上的文件了。通过原生js来动态创建type"file"的input元素&#xff0c;之后给监听该元素的…

【方法】如何把Excel“只读方式”变成可直接编辑?

Excel在“只读方式”下&#xff0c;编辑后是无法直接保存原文件的&#xff0c;那如何可以直接编辑原文件呢&#xff1f;下面来一起看看看吧。 如果Excel设置的是无密码的“只读方式”&#xff0c;那在打开Excel后&#xff0c;会出现对话框&#xff0c;提示“是否以只读方式打开…

生成随机数C++

随机数生成 在C中&#xff0c;有多种方式可以生成和输出随机数。以下是几种常见的方式&#xff1a; 1. 使用 <cstdlib> 头文件中的 rand() 函数&#xff1a; #include <iostream> #include <cstdlib> #include <ctime>int main() {// 设置种子&…

什么是甘特图?谁还不知道?做管理的来看看!

在现代商业社会&#xff0c;项目管理已成为不可或缺的技能。而甘特图作为一种强大的项目管理工具&#xff0c;正逐渐受到越来越多人的青睐。那么&#xff0c;什么是甘特图&#xff1f;又有什么工具可以绘制甘特图呢&#xff1f;本文将为你一一解答。 一、甘特图的定义 甘特图…

gin会话控制篇 - Cookie和Session

1. Cookie介绍 HTTP是无状态协议&#xff0c;服务器不能记录浏览器的访问状态&#xff0c;也就是说服务器不能区分两次请求是否由同一个客户端发出Cookie就是解决HTTP协议无状态的方案之一&#xff0c;中文是小甜饼的意思Cookie实际上就是服务器保存在浏览器上的一段信息。浏览…

Unity - 简单音频视频

“Test_04” 音频 使用AudioTest脚本控制Audio Source组件&#xff0c;在脚本中声明"music"和"se"之后&#xff0c;在unity中需要将音频资源拖拽到对应位置。 AudioTest public class AudioTest : MonoBehaviour {// 声明音频// AudioClippublic AudioC…

Vulnhub-dc4

靶场下载 https://download.vulnhub.com/dc/DC-4.zip 信息收集 判断目标靶机的存活地址: # nmap -sT --min-rate 10000 -p- 192.168.1.91 -oN port.nmap Starting Nmap 7.94 ( https://nmap.org ) at 2024-01-21 16:36 CST Stats: 0:00:03 elapsed; 0 hosts completed (1 up…

三、MySQL之创建和管理表

一、基础知识 1.1 一条数据存储的过程 存储数据是处理数据的第一步 。只有正确地把数据存储起来,我们才能进行有效的处理和分析。否则,只 能是一团乱麻,无从下手。 在 MySQL 中, 一个完整的数据存储过程总共有 4 步,分别是创建数据库、确认字段、创建数据表、插入数据。 …

在字节5年被优化,太难了。。。

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 先简单说下&#xff0c;涵哥是某不知名 985 的本硕&#xff0c;17 年毕业加入字节&#xff0c;以…