服务器搭建(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 事件。它提供了高效的事件驱动模型,可以大大提升程序的性能和可扩展性。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

爬楼梯Java(斐波那契数列)

题目:有n阶楼梯,一次只能爬一层或者两层,请问有多少种方法? 这类题目其实都可以用斐波那契数列来解决,比如: 一阶楼梯只有一种方法 二阶楼梯有(11,2)两种方法 三阶楼梯有(111,12,21)三种方法 四阶楼梯有(1111,121,112,22,211)五种方式 五阶楼梯有(11111,1112,122,1211,1…

Servlet执行流程生命周期方法介绍体系结构、Request和Response的功能详解

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 Servlet 一、 Servlet执行流程二、Servlet生…

【ONE·Linux || 进程间通信】

总言 进程间通信&#xff1a;简述进程间通信&#xff0c;介绍一些通信方式&#xff0c;管道通信&#xff08;匿名、名命&#xff09;、共享内存等。 文章目录 总言1、进程间通信简述2、管道2.1、简介2.2、匿名管道2.2.1、匿名管道的原理2.2.2、编码理解&#xff1a;用fork来共…

Linux 系统移植(二)--系统调试

文章目录 一、 编译文件系统1.1 下载资源安装包1.2 配置模板ARM64目标平台1.3 配置交叉编译器1.4 配置登录用户名和密码1.5 配置Linux 控制台1.6 配置文件系统格式1.7 编译buildroot文件系统 二、编译ARM64 Linux三、启动 Qemu Linux系统参考链接&#xff1a; 一、 编译文件系统…

9.基于粤嵌gec6818开发板小游戏2048的算法实现

2048源码&#xff1a; 感兴趣的可以去了解一下2048优化算法&#xff1a; 基于蒙特卡罗树搜索的_2048_游戏优化算法_刘子正 #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/mman.h> #incl…

机器学习笔记:adaBoost

1 介绍 AdaBoost&#xff08;Adaptive Boosting&#xff09;是一种集成学习方法&#xff0c;它的目标是将多个弱分类器组合成一个强分类器 通过反复修改训练数据的权重&#xff0c;使得之前分类错误的样本在后续的分类器中得到更多的关注每一轮中&#xff0c;都会增加一个新的…

大模型时代,如何找准AI发展新方向?TVP读书会与你共探

引言 随着 ChatGPT 掀起人工智能的发展狂潮&#xff0c;大模型时代悄然来临。大模型的出现让人们看到了人工智能在某些方面几乎接近人类智能的“威力”&#xff0c;也由此使人们产生了关于 AGI 的期盼与隐忧。其中&#xff0c;关于大模型是否是人工智能发展的拐点、科技从业者又…

数据结构--堆排序

目录 堆的定义 建立初始化堆的步骤 建立大根堆的代码 大根堆排序的代码 算法效率分析 稳定性 堆的定义 回忆 基于选择排序的特性&#xff1a;选取关键字最小&#xff08;或者最大&#xff09;的元素放入到序列里面&#xff0c;知道了大堆和小堆概念&#xff0c;所以将…

尚硅谷大数据项目《在线教育之实时数仓》笔记002

视频地址&#xff1a;尚硅谷大数据项目《在线教育之实时数仓》_哔哩哔哩_bilibili 目录 第06章 数据仓库环境准备 P006 P007 P008 P009 P010 P011 P012 P013 P014 第06章 数据仓库环境准备 P006 P007 P008 http://node001:16010/master-status [atguigunode001 ~]$ …

Qt --- Day03

<?xml version"1.0" encoding"UTF-8"?> <ui version"4.0"><class>Widget</class><widget class"QWidget" name"Widget"><property name"geometry"><rect><x>0…

Java 调用 GitLabAPI 获取仓库里的文件件 提交记录

1. 需求 项目组 需要做统计&#xff0c;获取每个开发人员的代码提交次数&#xff0c;提交时间&#xff0c;提交人等等&#xff0c;因代码在GitLab上管理&#xff0c;所以需要调用GitLabAPI来获取。 2. 开发 API官网&#xff1a;https://docs.gitlab.com/ee/api/ 2.1 创建自…

3D科研绘图与学术图表绘制:从入门到精通

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 3D科研绘图和学术图表绘…

interview6-jvm篇

JVM(Java Virtual Machine)Java程序的运行环境&#xff08;java二进制字节码的运行环境&#xff09; 在JVM中共有四大部分&#xff0c;分别是ClassLoader&#xff08;类加载器&#xff09;、Runtime DataArea&#xff08;运行时数据区&#xff0c;内存分区&#xff09;、Execu…

分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测(SE注意力机制)

分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测&#xff08;SE注意力机制&#xff09; 目录 分类预测 | MATLAB实现WOA-CNN-BiGRU-Attention数据分类预测&#xff08;SE注意力机制&#xff09;分类效果基本描述模型描述程序设计参考资料 分类效果 基本描述 1.MATLA…

【Synapse数据集】Synapse数据集介绍和预处理,数据集下载网盘链接

【Segment Anything Model】做分割的专栏链接&#xff0c;欢迎来学习。 【博主微信】cvxiaoyixiao 本专栏为公开数据集的介绍和预处理&#xff0c;持续更新中。 文章目录 1️⃣Synapse数据集介绍文件结构源文件样图文件内容 2️⃣Synapse数据集百度网盘下载链接官网下载登录下…

TCPIP状态转换

一个TCP连接在其生命周期中经过了一系列的状态跃迁。一个TCP连接的状态包括&#xff1a; LISTEN &#xff1a;表示正在等待来自任何远程TCP和端口的连接请求&#xff0c;调用listen后套接字出于监听状态SYN_SENT : 表示在发送了连接请求后&#xff0c;正在等待匹配的连接请求…

【Linux is not Unix】Linux前言

目录 二战军工的产物——第一台现代电子数字计算机ENIAC&#xff08;埃尼阿克&#xff09; Unix Linux Linux企业应用现状 如今计算机已经应用在我们生活的各个层面&#xff0c;像我们日常使用的笔记本是计算机的一类&#xff0c;可以解决我们生活中遇到的很多问题&#xff…

嵌入式MCU都有什么高级用法?

嵌入式MCU都有什么高级用法&#xff1f; 您举的几个例子&#xff0c;确实是MCU外设的一些高端玩法。只是不知道您是否想过&#xff0c;既然这些机制是被 人设计出来的&#xff0c;那它就是种标准用法。从微控制器的发展历程来看&#xff0c;许多硬件机制都是有了实际 需求后才…

字节8年经验之谈 —— 10大自动化测试框架总结!

软件行业正迈向自主、快速、高效的未来。为了跟上这个高速前进的生态系统的步伐&#xff0c;必须加快应用程序的交付时间&#xff0c;但不能以牺牲质量为代价。快速实现质量是必要的&#xff0c;因此质量保证得到了很多关注。为了满足卓越的质量和更快的上市时间的需求&#xf…

大屏大概是怎么个开发法(前端)

写在前面&#xff0c;博主是个在北京打拼的码农&#xff0c;从事前端工作5年了&#xff0c;做过十多个大大小小不同类型的项目&#xff0c;最近心血来潮在这儿写点东西&#xff0c;欢迎大家多多指教。 对于文章中出现的任何错误请大家批评指出&#xff0c;一定及时修改。有任何…