服务器搭建(TCP套接字)-epoll版(服务端)
epoll 是一种在 Linux 系统上用于高效事件驱动编程的 I/O 多路复用机制。它相比于传统的 select 和 poll 函数具有更好的性能和扩展性。
epoll 的主要特点和原理:
1、事件驱动:epoll 是基于事件驱动的模型,它通过监听事件来触发相应的回调函数,而不是像传统的阻塞模型那样持续轮询。这样可以避免无效的轮询操作,提高效率。
2、高效:epoll 使用了红黑树(rbtree)和哈希表(hash table)的数据结构来存储和管理大量的文件描述符,使得在大规模连接的情况下,对文件描述符的管理和查找操作具备较高的效率。
3、边缘触发:epoll 提供了边缘触发(edge-triggered)的工作模式。在边缘触发模式下,只有当文件描述符的状态发生变化时,epoll 才会通知应用程序。这使得应用程序能够更精确地处理事件,避免了事件的丢失和重复触发。
4、扩展性:epoll 支持高并发的连接,可以同时监听大量的文件描述符,且不随文件描述符数量的增加而性能下降。它采用了事件通知的方式,只有在文件描述符发生状态变化时才会通知应用程序,避免了大量的轮询操作。
使用 epoll 的基本步骤如下:
1、创建 epoll 实例,通过调用 epoll_create 函数创建一个 epoll 对象。
2、将需要监听的文件描述符加入 epoll 实例,通过调用 epoll_ctl 函数将文件描述符添加到 epoll 中,并指定需要监听的事件类型。
epoll_ctl 是一个用于控制 epoll 实例的系统调用函数,它用于向 epoll 实例中添加、修改或删除文件描述符及其关联的事件。
3、 进入事件循环,调用 epoll_wait 函数等待事件发生。该函数会阻塞程序执行,直到有事件发生或超时。
epoll_wait 是一个用于等待事件的系统调用函数,它在 epoll 实例上进行阻塞等待,直到有事件就绪或超时。
4、 当 epoll_wait 返回时,根据返回的就绪事件进行相应的处理。可以通过遍历返回的事件列表来获取就绪的文件描述符和事件类型。
epoll 在网络编程中广泛应用,特别适用于高并发的服务器开发,能够处理大量的并发连接和高频率的 I/O 事件。它提供了高效的事件驱动模型,可以大大提升程序的性能和可扩展性。
一、epoll_create
int epoll_create(int size);
epoll_create 函数接受一个参数 size,该参数指定了 epoll 实例所能处理的最大文件描述符数目。它返回一个整数值,表示 epoll 实例的文件描述符,用于后续的 epoll 相关操作。
入参:
size 参数是一个提示值,用于告诉内核 epoll 实例需要处理的最大文件描述符数目。内核会根据此提示值进行一些优化,但实际上该值在大多数情况下并不会限制 epoll 实例所能处理的文件描述符数目。
返回值:
- 成功时返回一个非负整数,表示 epoll 实例的文件描述符。
- 如果调用失败,返回值为 -1,并设置相应的错误码,可以通过 errno 来获取具体的错误信息。
epoll_create 函数创建的 epoll 实例是默认的边缘触发模式(Edge Triggered Mode)。这意味着当文件描述符上的事件状态从未就绪变为就绪时,epoll 会返回该事件,而不是只在事件状态为就绪时返回一次。
在使用完 epoll 实例后,应当使用 close 函数显式关闭 epoll 实例的文件描述符,以释放相关资源。
二、epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
入参:
- epfd:表示 epoll 实例的文件描述符,即通过 epoll_create 创建的返回值。
- op:表示要进行的操作类型,可以是以下三种值之一:
- EPOLL_CTL_ADD:将文件描述符 fd 添加到 epoll 实例中,关联的事件由 event 参数指定。
- EPOLL_CTL_MOD:修改已添加到 epoll 实例中的文件描述符 fd 的关联事件,新的事件由 event 参数指定。
- EPOLL_CTL_DEL:从 epoll 实例中删除文件描述符 fd。
- fd:表示要添加、修改或删除的文件描述符。
- event:指向一个 struct epoll_event 结构体的指针,用于设置文件描述符的关联事件。
event 字段表示要关注的事件类型,可以是以下事件类型的组合:- EPOLLIN:表示可读事件。
- EPOLLOUT:表示可写事件。
- EPOLLRDHUP:表示对端关闭连接或关闭写端。
- EPOLLPRI:表示有紧急数据可读。
- EPOLLERR:表示发生错误。
- EPOLLHUP:表示连接关闭。
- EPOLLET:使用边缘触发模式(Edge Triggered Mode)。
- EPOLLONESHOT:在事件触发后,将文件描述符从 epoll 实例中删除,需要重新添加才能再次触发。
struct epoll_event {__uint32_t events; // 表示要关注的事件类型epoll_data_t data; // 用户数据,可以是文件描述符或指针
};typedef union epoll_data {void *ptr; // 指针类型的用户数据int fd; // 文件描述符类型的用户数据__uint32_t u32;__uint64_t u64;
} epoll_data_t;
返回值:
- 成功时返回 0
- 失败时返回 -1,并设置相应的错误码,可以通过 errno 来获取具体的错误信息。
三、epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
入参:
- epfd:表示 epoll 实例的文件描述符,即通过 epoll_create 创建的返回值。
- events:指向一个 struct epoll_event 数组的指针,用于存储就绪的事件信息。
- maxevents:表示 events 数组的大小,即最多可以存储的事件数目。
- timeout:表示等待的超时时间,以毫秒为单位。可以是以下值之一:
- -1:表示永久阻塞,直到有事件就绪。
- 0:表示非阻塞,立即返回。
- 大于 0:表示超时时间,等待指定的毫秒数后返回。
返回值:
- epoll_wait 函数用于阻塞等待 epoll 实例上的事件就绪。当有事件就绪时,它将填充 events 数组,并返回就绪事件的数量。
- 如果 epoll_wait 函数返回值大于 0,则表示有就绪事件,并且可以通过遍历 events 数组来获取每个就绪事件的相关信息。
- 如果 epoll_wait 函数返回值为 0,表示超时时间到达,即没有事件就绪。
- 如果 epoll_wait 函数返回值为 -1,表示调用出错,可以通过 errno 来获取具体的错误信息。
四、代码实现
#include <iostream>
//socket
#include <sys/types.h>
#include <sys/socket.h>
//close
#include <unistd.h>
//exit
#include <stdlib.h>
//perror
#include <stdio.h>
//memset
#include <string.h>
//htons
#include <arpa/inet.h>
/* According to earlier standards */
#include <sys/time.h>
//epoll
#include <sys/epoll.h>#define PORT 8596
#define MESSAGE_SIZE 1024
#define FD_SIZE 1024
#define MAX_EVENTS 20
#define TIME_OUT 500int main(){int ret=-1;int socket_fd=-1;int accept_fd=-1;int backlog=10;int flags=1;struct sockaddr_in local_addr,remote_addr;struct epoll_event ev,events[FD_SIZE];int epoll_fd=-1;int event_number=0;//create socketsocket_fd=socket(AF_INET,SOCK_STREAM,0);if(socket_fd == -1){perror("create socket error");exit(1);}//set option of socketret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));if ( ret == -1 ){perror("setsockopt error");}//set socket addresslocal_addr.sin_family=AF_INET;local_addr.sin_port=htons(PORT);local_addr.sin_addr.s_addr=INADDR_ANY;bzero(&(local_addr.sin_zero),8);//bind socketret=bind(socket_fd, (struct sockaddr *)&local_addr,sizeof(struct sockaddr_in));if(ret == -1){perror("bind socket error");exit(1);}ret=listen(socket_fd, backlog);if(ret ==-1){perror("listen error");exit(1);}//创建epollepoll_fd=epoll_create(256);ev.data.fd=socket_fd;ev.events=EPOLLIN;//将socket_fd加入到epoll中epoll_ctl(epoll_fd,EPOLL_CTL_ADD,socket_fd,&ev);//loop to accept clientfor(;;){event_number=epoll_wait(epoll_fd,events,MAX_EVENTS,TIME_OUT);for(int i=0;i<event_number;i++){if(events[i].data.fd==socket_fd){socklen_t addrlen = sizeof(remote_addr);accept_fd=accept(socket_fd,( struct sockaddr *)&remote_addr, &addrlen);ev.data.fd=accept_fd;ev.events=EPOLLIN | EPOLLET;//添加accept_fd到epoll中//添加accept_fd到epoll中if((epoll_ctl(epoll_fd,EPOLL_CTL_ADD,accept_fd,&ev))==-1){close(accept_fd);} }else if(events[i].events & EPOLLIN){//有数据可读char in_buf[MESSAGE_SIZE];memset(in_buf, 0, MESSAGE_SIZE);//receive dataret = recv( events[i].data.fd, &in_buf, MESSAGE_SIZE, 0 );if(ret <= 0){switch (errno){case EAGAIN: //暂时没有数据break;case EINTR: //被终断ret = recv(events[i].data.fd, &in_buf, MESSAGE_SIZE, 0);break;default:printf("the client is closed, fd:%d\n", events[i].data.fd);epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, &ev);close(events[i].data.fd);break;} } printf("receive message:%s\n", in_buf);send(events[i].data.fd, &in_buf, ret, 0);}}}printf("quit server....");// 关闭监听 socket 和 epoll 实例close(socket_fd);close(epoll_fd);return 0;
}
- 服务端
- 客户端1
- 客户端2
从代码直观就能看出epoll的代码量和逻辑实现相比select,都特别简洁。epoll 在网络编程中广泛应用,特别适用于高并发的服务器开发,能够处理大量的并发连接和高频率的 I/O 事件。它提供了高效的事件驱动模型,可以大大提升程序的性能和可扩展性。
相关文章:

服务器搭建(TCP套接字)-epoll版(服务端)
epoll 是一种在 Linux 系统上用于高效事件驱动编程的 I/O 多路复用机制。它相比于传统的 select 和 poll 函数具有更好的性能和扩展性。 epoll 的主要特点和原理: 1、事件驱动:epoll 是基于事件驱动的模型,它通过监听事件来触发相应的回调函…...
第一章:最新版零基础学习 PYTHON 教程(第十八节 - Python 表达式语句–Python 中的中断、继续和传递)
在 Python 中使用循环可以高效地自动执行和重复任务。但有时,可能会出现您想要完全退出循环、跳过迭代或忽略该条件的情况。这些可以通过循环控制语句来完成。循环控制语句改变其正常顺序的执行。当执行离开作用域时,在该作用域中创建的所有自动对象都将被销毁。Python支持以…...

Spring Cloud Alibaba Ribbon负载均衡器
文章目录 Ribbon 负载均衡器环境搭建1.依赖2.配置3.修改其默认的负载均衡策略3.1 验证 4.创建自定义的Rule4.1 MyRule()4.2 在配置config类中配置 5.饥饿加载6.我只想访问不想被别的访问 Ribbon 负载均衡器 背景 Ribbon 是一个用于客户端负载均衡的开源…...

ardupilot开发 ---传感器驱动,外设驱动篇
ardupilot支持不同厂商的传感器,如雷达,声呐,激光,相机等; 支持的通信协议 I2C, SPI, UART (aka Serial) CANBUS 驱动程序的前后台分离 ardupilot中传感器驱动的重要结构是前后分离; Library库调用前端…...

二叉树的存储
目录 1.使用孩子表示法创建二叉树 2.二叉树的遍历 2.1前中后序遍历 2.2 前中后序遍历的选择题 2.3实现前中后序遍历 2.3.1前序遍历 2.3.2中序遍历 2.3.3后序遍历 3.二叉树的基本操作 3.1获取叶子节点的个数 3.2获取树中节点的个数 3.3获取第K层节点的个数 3.4获取…...
List 去重的几种方法
🔔HashSet去重 import java.util.HashSet;HashSet<Integer> set new HashSet<>(); set.add(1); set.add(2); set.add(2); System.out.println(set); // [1, 2]🔔TreeSet去重 import java.util.TreeSet;TreeSet<Integer> set new T…...
UNet网络制作
UNet网络制作 代码参考UNet数据集制作及代码实现_哔哩哔哩_bilibili,根据该UP主的代码,加上我的个人整理和理解。(这个UP主的代码感觉很好,很规范 UNet网络由三部分组成:卷积块,下采样层,上采样…...

智能热水器丨打造智能家居新体验
随着科学技术的不断发展,智能电器越来越被大众所采纳,如智能扫地机,智能洗衣机,智能微波炉等等,越来越智能的电器为人们的生活带来了许多便利。以往的热水器一般都是只有按键/机械的控制方式,没有其他无线控…...
Python 十进制转化二进制1.0(简易版)
"""十进制转换二进制知识点:1、循环语句/跳转语句 while/break2、运算符 求余%、整除//3、字符串拼接4、字符串切片5、数据类型转换不足与改善:1、不能输入非正整数,否则报错或卡住"""# 倒序二进制存储 revers…...

WebGL 选中一个表面
目录 选中一个表面 示例程序(PickFace.js) 代码详解 gl.readPixels()见126行效果 gl.UNSIGNED_BYTE注意点 示例效果 选中一个表面 WebGL 选中物体_山楂树の的博客-CSDN博客可以使用同样的方法来选中物体的某一个表面。这一节在Pi…...

open ai chartgpt 安装插件 txyz.ai
1 chatgpt 页面 左下角 用户 -> setting 2 3...

【算法思想】贪心
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…...
freeswitch-01
文章目录 1. 电话实现技术2. 模拟信号与数字信号2.1 模拟信号2.2 数字信号 3. PCM4. 局间中继与电路复用技术5. 信令5.1 定义5.2 分类5.2.1 功能分类5.2.2 工作区域分类5.2.3 信道分类 5.3 用户线信令5.4 局间信令5.5 七号信令5.6 H.323与SIP信令 6. 媒体6.1 定义 7. 电路交换与…...

Zookeeper-集群介绍与核心理论
Zookeeper集群 4.Zookeeper集群4.1) 介绍4.2) 核心理论 4.Zookeeper集群 4.1) 介绍 Leader选举: Serverid:服务器ID。比如有三台服务器,编号分别是1,2,3。编号越大在选择算法中的权重越大。Zxid:数据ID。服务器中存放的最大数据…...
动态分配的内存位置在哪里?
在C++中,动态分配的内存位于称为堆(Heap)的内存区域。以下是一些关于堆和其他相关内存区域的基本信息: 堆(Heap): 这是一个用于动态内存分配的内存区域。使用new(C++)或malloc(C)等函数从堆中分配内存,并使用delete(C++)或free(C)释放这些内存。堆的大小通常受…...
Vue3中的Ref与Reactive:深入理解响应式编程
前言 Vue 3是一个功能强大的前端框架,它引入了一些令人兴奋的新特性,其中最引人注目的是ref和reactive。这两个API是Vue 3中响应式编程的核心,本文将深入探讨它们的用法和差异。 什么是响应式编程? 在Vue中,响应式编…...

Windows10/11显示文件扩展名 修改文件后缀名教程
前言 写这篇文章的原因是由于我分享的教程中的文件、安装包基本都是存在阿里云盘的,下载后需要改后缀名才能使用。 但是好多同学不会改。。 Windows 10 随便打开一个文件夹,在上方工具栏点击 “查看”点击 “查看” 后下方会显示更详细的工具栏然后点…...

【C++】手撕string(string的模拟实现)
手撕string目录: 一、 Member functions 1.1 constructor 1.2 Copy constructor(代码重构:传统写法和现代写法) 1.3 operator(代码重构:现代写法超级牛逼) 1.4 destructor 二、Other mem…...
用python3编译cv_bridge
文章目录 概要依赖工作空间编译可能遇到的问题error: option --install-layout not recognized概要 当我在编写一个使用传感器图像传输和OpenCV4的ROS包时,从构建到编译代码的一切都很顺利。当我开始运行节点本身时,问题出现了,它给出了以下错误: Assertion failed (tlsSl…...

招商信诺人寿基于 Apache Doris 统一 OLAP 技术栈实践
本文导读: 当前,大数据、人工智能、云计算等技术应用正在推动保险科技发展,加速保险行业数字化进程。在这一背景下,招商信诺不断探索如何将多元数据融合扩充,以赋能代理人掌握更加详实的用户线索,并将智能…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...