【TCP/IP】利用I/O复用技术实现并发服务器 - epoll

目录

select的缺陷

epoll函数

epoll_create

epoll_ctl

epoll_wait

基于epoll的回声服务器实现


select的缺陷

        在之前,我们使用了select函数完成了对回声服务器端I/O的复用,但是从代码上依然存有缺陷,主要集中在:

  • 每次调用select函数需向函数传递监视对象信息
  • 调用select后,要在fd_set中设计循环语句以监视所有对象变量

       这两项操作对于性能损耗较大。并且需要注意的是,套接字是由操作系统管理,在需要频繁调用select函数的场景下,select函数要求必须向其传递监视对象(套接字),那么这样时间、空间都会产生巨大耗费。

        不过,这个问题可以通过合适的策略来进行优化——仅向操作系统传递1次监视对象文件描述符,当监视对象有变化时只通知关注的事件。

        在Linux中,epoll可以支持这项操作。

epoll函数

* 2.5.44版本之前的Linux内核无法使用epoll函数

        在实现epoll时,需要用到其他一些关联函数和结构体:

  • epoll_create: 创建保存epoll文件描述符的空间。
  • epoll_ctl: 向空间注册并注销文件描述符。
  • epoll_wait: 与select函数类似,等待文件描述符发生变化。

        相较于select方式中使用fd_set变量来存储监视对象文件描述符,epoll使用epoll_create函数来向操作系统请求保存对象文件描述符。

        select方法中需要使用到 FD_SET 和 FD_CLR 宏函数,而在epoll方法中只需要通过 epoll_ctl 函数请求操作系统完成即可。 

        在监视文件描述符的变化方面,epoll调用epoll_wait函数来实现对文件描述符变化的监视,同时用结构体类型epoll_event将发生变化的文件描述符集中于一起。

        epoll_event结构体类型定义如下:

typedef union epoll_data
{void *ptr;int fd;uint32_t u32;uint64_t u64;
} epoll_data_t;struct epoll_event
{uint32_t events;	/* 欲监视的 Epoll 事件 */epoll_data_t data;	/* 用户数据变量 */
}

        声明epoll_event结构体后,将其传递给epoll_wait函数,发生变化的文件描述符将被填入该结构体中,而无需再采用循环的方式对范围内所有文件描述符进行扫描监视。

epoll_create

        epoll_create函数用以创建epoll实例,其引用头文件和函数结构如下:

#include <sys/epoll.h>int epoll_create(int size);/* 成功时返回 epoll 文件描述符,失败时返回-1。参数size 用来传递epoll实例的大小 */

        需要注意的是,函数中参数size的值并不能控制epoll实例的实际大小,该值只负责告诉操作系统实例大小的建议值,供操作系统参考。

        epoll_create函数创建的实例本质上是套接字,因此也会返回文件描述符,同时在结束对套接字的操作时,要调用close函数来进行关闭。

拓展:

        Linux2.6.8之后的内核版本会忽略epoll_create中的size参数意义,即size参数不再具有实际意义,操作系统将会自行根据情况调整epoll实例的大小。

epoll_ctl

        创建epoll实例后,需要通过epoll_ctl函数来对监视对象文件描述符进行注册

        epoll_ctl函数的引用头文件和函数结构如下:

#include <sys/epoll.h>int epoll_ctl(int epfd , int op , int fd , struct epoll_event * event);//成功时返回 0 ,失败时返回 -1/* 参数含义 */
/* epfd: 用于注册监视对象epoll实例的文件描述符op: 用于指定监视对象的添加、删除或修改等操作fd: 需要注册的监视对象文件描述符event: 监视对象的时间类型
*/

        其中参数op有设计好的宏,用来表示增加、删除或修改操作:

  • EPOLL_CTL_ADD: 将文件描述符注册到epoll实例 
  • EPOLL_CTL_DEL:  删除epoll实例中的文件描述符 
  • EPOLL_CTL_MOD: 更改已注册的文件描述符中所关注的事件发生情况

        使用EPOLL_CTL_DEL时,应向第四个参数(event)中传递NULL。

示例:

//从实例 A 中删除文件描述符SOCK_1
epoll_ctl(A , EPOLL_CTL_DEL , SOCK_1, NULL);//向实例 A 中注册文件描述符SOCK_1,并监视 EVENT中 EPOLLIN 事件的发生与否
struct epoll_event EVENT;
EVENT.events=EPOLLIN;
EVENT.data.fd=sockfd;
epoll_ctl(A , EPOLL_CTL_ADD , SOCK_1, &EVENT);

        示例中EPOLLIN是一种事件类型,用以表示“需要读取数据的情况”,还有其他事件类型,具体如下:

  • EPOLLIN: 需要读取数据的情况 。
  • EPOLLOUT: 输出缓冲为空,可立即发送数据的情况 。
  • EPOLLPRI: 收到OOB数据的情况 。
  • EPOLLRDHUP: 断开连接或半关闭的情况(常用在边缘触发方式下) 。
  • EPOLLERR: 发生错误的情况 。
  • EPOLLET: 以边缘触发的方式得到事件通知 。
  • EPOLLONESHOT: 发生一次事件后,相应文件描述符不再收到事件通知的情况。(需要向epoll_ctL函数的第二个参数传递EPOLL_CTL_MOD,再次设置事件)

epoll_wait

        epoll_wait用于完成最后对对象文件描述符的监视

        epoll_wait函数的引用头文件和函数结构如下:

#include <sys/epoll.h>int epoll_wait(int epfd , struct epoll_event * events , ïnt maxevents , int
timeout);// 成功时返回发生事件的文件描述符数,失败时返回 -1。/* 参数含义 */
/* epfd: 表示事件发生监视范围的 epoll 实例 的文件描述符events: 保存发生事件的文件描述符集合的结构体地址值maxevents: 可以保存的最大事件数 (对应第二个参数events)timeout: 以 1/ 1000秒为单位的等待时间,传递 -1 时代表一直等待到事件的发生。
*/

        需要注意的是,epoll_wait函数中的events变量(第二个参数)需要用malloc来开辟空间。

比如:

struct epoll_event * EVENTS;//EPOLL_SIZE是宏常量,其值代表欲开辟的实例数。注意最后要对类型进行强转,因为malloc返回的是void*
EVENTS = (struct epoll_event *) malloc(sizeof(struct epoll_event) * EPOLL_SIZE); epoll_wait(SOCK, EVENTS, EPOLL_SIZE, -1)

        接下来让我们尝试用epoll来实现之前的回声服务器

基于epoll的回声服务器实现

代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>#define BUF_SIZE 1024
#define EPOLL_SIZE 5void Sender_message(char *message)
{puts(message);exit(1);
}int main(int argc, char *argv[])
{int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;socklen_t adr_sz;int str_len, i;char buf[BUF_SIZE];struct epoll_event *ep_events;struct epoll_event event;int epfd, event_cnt;serv_sock = socket(PF_INET, SOCK_STREAM, 0);if (serv_sock == -1)Sender_message((char *)"sock creation error");memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)Sender_message((char *)"bind error");if (listen(serv_sock, 5) == -1)Sender_message((char *)"listen error");epfd = epoll_create(EPOLL_SIZE);ep_events = (struct epoll_event *)malloc(sizeof(struct epoll_event) * EPOLL_SIZE);event.events = EPOLLIN; //声明欲监视的事件为需要读取数据的情况event.data.fd = serv_sock; //将服务器端套接字保存至epoll_event结构体中epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);while (1){event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);if (event_cnt == -1){puts((char *)"supervise error");break;}for (i = 0; i < event_cnt; i++){if (ep_events[i].data.fd == serv_sock) //若找到的文件描述符是服务器端的{adr_sz = sizeof(clnt_adr);clnt_sock =accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz);event.events = EPOLLIN;event.data.fd = clnt_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);printf((char *)"connected client: %d \n", clnt_sock);}else{str_len = read(ep_events[i].data.fd, buf, BUF_SIZE);if (str_len == 0) // 关闭请求{epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);close(ep_events[i].data.fd);printf((char *)"closed client: %d \n", ep_events[i].data.fd);}else{write(ep_events[i].data.fd, buf, str_len); //写数据}}}}close(serv_sock);close(epfd);return 0;
}

运行结果:

 得到验证

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

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

相关文章

微服务_Nacos

简介 Nacos&#xff08;全称为“动态服务发现、配置和服务管理平台”&#xff09;是阿里巴巴开源的一款云原生服务发现和配置管理平台&#xff0c;支持多种语言。它提供了服务发现、配置管理、服务治理和流量管理等功能&#xff0c;使得微服务系统的构建和管理更加便捷。Nacos…

前端食堂技术周刊第 89 期:ES 2023、MDN Playground、TS 5.2 Beta、逆向分析 GitHub Copilot

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;糯米糍荔枝 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看…

多元分类预测 | Matlab基于灰狼优化深度置信网络(GWO-DBN)的分类预测,多特征输入模型,GWO-DBN分类预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元分类预测 | Matlab基于灰狼优化深度置信网络(GWO-DBN)的分类预测,多特征输入模型,GWO-DBN分类预测 多特征输入单输出的二分类及多分类模型。程序内注释详细,直接替换数据就可以用。程序语言为matlab,程序可…

apple pencil值不值得购买?ipad可以用的手写笔推荐

现在市面的电容笔品牌鱼龙混杂&#xff0c;我们很在选购中很容易就踩坑&#xff0c;例如买到一些书写会频繁出现断触的&#xff0c;或者防误触功能会失灵。所以我们在选购中务必要擦亮双眼。而对于一些将ipad作为一种学习工具的人而言&#xff0c;电容笔已经是iPad中不可或缺的…

0124 计算机网络体系结构

目录 1.计算机网络体系结构 1.1计算机网络概述 计算机网络的组成 计算机网络的功能 计算机网络的分类 计算机网络的性能指标 1.1部分习题 1.2计算机网络体系结构与参考模型 计算机网络分层结构 计算机网络协议、接口与服务 ISO/OSI参考模型和TCP/IP模型 OSI参考模型…

UE5.1.1 C++从0开始(17.GAS游戏能力系统)

教程的链接&#xff1a;https://www.bilibili.com/video/BV1nU4y1X7iQ 教程内的老师没用GAS的插件&#xff0c;而是自己写了一个。这一篇文章只是开头&#xff0c;还有很多的内容没有往里面写。 新增了一个object类&#xff0c;新增了一个使用这个类的组件。然后把这个组件用…

Spring高手之路4——深度解析Spring内置作用域及其在实践中的应用

文章目录 1. Spring的内置作用域2. singleton作用域2.1 singleton作用域的定义和用途2.2 singleton作用域线程安全问题 3. prototype作用域3.1 prototype作用域的定义和用途3.2 prototype作用域在开发中的例子 4. request作用域&#xff08;了解&#xff09;5. session作用域&a…

NGINX+Tomcat负载均衡、动静分离集群

目录 前言 一、NGINX正向代理与反向代理 1.1、NGINX正向代理 1.2、NGINX反向代理 1. 2.1Nginx配置反向代理的主要参数 二、负载均衡 三、NGINX动静分离集群 3.1动静分离原理 四、NginxTomcat动静分离 4.1搭建nginx代理服务器192.168.14.100 4.1.1安装 NGINX依赖环境 …

阿里云国际站:云原生数据库2.0时代,阿里云如何将云原生进行到底?

【猎云网上海】11月3日报道&#xff08;文/孙媛&#xff09; “PolarDB将云原生进行到底&#xff01;” 在2021年云栖大会上&#xff0c;阿里巴巴集团副总裁、阿里云智能数据库事业部总负责人李飞飞宣布了PolarDB实现三层解耦的重磅升级以及引领云原生数据库技术持续创新的态…

【python】数据处理

1.按照间隔生成时间戳,并保存为csv文件 ##### 按照间隔生成时间戳,并保存为csv文件 import pandas as pd from datetime import datetime, time, timedelta times [] ts datetime(2023, 6, 17, 9, 10, 0) while ts < datetime(2023, 6, 17, 9, 26, 40):# times.append(t…

博客相关推荐在线排序学习实践

现有固定槽位的填充召回策略在相关线上推荐服务中缺乏有效的相关性排序&#xff0c;存在较硬的排列顺序&#xff0c;各个策略之间互相影响&#xff0c;导致线上基于规则的拓扑图比较复杂&#xff0c;因此设计在线推理服务&#xff0c;通过学习用户行为完成在线排序。 1. 博客相…

数通王国历险记之TCP协议下的三大协议的验证实验

系列文章目录 数通王国历险记&#xff08;1&#xff09; 前言 一&#xff0c;我们要先知道PDU是什么&#xff1f; 二、TCP协议下的三大协议的验证实验 1.FTP的验证实验 1&#xff0c;拓扑图 2.将lsw4配置一下 3&#xff0c;FTP服务器端开启FTP服务&#xff1a; 4&#x…