服务器搭建(TCP套接字)-epoll版(服务端)

news2024/12/28 20:54:37

   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 500

int 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 socket
  socket_fd=socket(AF_INET,SOCK_STREAM,0);
  if(socket_fd == -1){
    perror("create socket error");
    exit(1);
  }
 //set option of socket
  ret = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
  if ( ret == -1 ){
    perror("setsockopt error");
  }

 //set socket address
  local_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 socket
 ret=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);
  }
  //创建epoll
  epoll_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 client
  for(;;){
   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 data
        ret = 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.coloradmin.cn/o/1033944.html

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

相关文章

七分钟,数据转换器get到了

全文阅读时间 | 预计七分钟 KING BASE 开源 OR 闭源&#xff1f; 在瞬息多变的软件市场上&#xff0c;开源还是闭源是一个恒久不变的话题。开源软件得益于基础架构和基本功能的全面开放&#xff0c;开发者能自由使用和二次开发&#xff0c;但使用前提是需要投入大量成本对软件进…

centos 7.9系统安装向日葵

1.下载地址 向日葵远程控制app官方下载 - 贝锐向日葵官网 2.下载依赖 yum install -y libappindicator-gtk3 安装好依赖之后&#xff0c;然后再安装向日葵软件 3.安装软件 sudo rpm -ivh 文件名.rpm 4.安装成功之后的位置

【Android Framework系列】第16章 存储访问框架 (SAF)

1 概述 Android 4.4&#xff08;API 级别 19&#xff09;引入了存储访问框架 (Storage Access Framework)。SAF让用户能够在其所有首选文档存储提供程序中方便地浏览并打开文档、图像以及其他文件。 用户可以通过易用的标准 UI&#xff0c;以统一方式在所有应用和提供程序中浏…

【神印王座】龙皓晨竟然上了头版头条!内容违背,新闻真实性原则

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析神印王座国漫。 大家有没有发现&#xff0c;当龙皓晨他们从驱魔关回到圣城时&#xff0c;有这么一幕&#xff0c;一个卖报小孩边走边说&#xff1a;驱魔关大捷&#xff0c;少年英雄龙皓晨操控守护与怜悯之神印王座&#x…

使用Pugixml库,轻松处理XML文件

文章作者&#xff1a;里海 来源网站&#xff1a;王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C-CSDN博客 目录 1.介绍 2.Pugixml库 3.配置Visual Studio开发环境 4.节点 5.常见的节点类型 6.命名空间pugi 7.xml_document装载整个XML文档结构 8.xml_node文…

WeCanStudio工具套件介绍

直接上视频,在超燃的《天马座幻想》的背景音乐下&#xff0c;再次了解一下该工具套件吧。 WeCanStudio开发套件介绍

setrlimit限制进程的cpu使用时间

linux可以用setrlimit进行资源限制&#xff0c;今天说的是setrlimit限制进程的cpu使用时间 代码如下&#xff1a; #include <sys/time.h> #include <sys/resource.h> #include <unistd.h>int main() {struct rlimit rlim;rlim.rlim_cur 10;rlim.rlim_max …

星际争霸之小霸王之小蜜蜂(十四)--资本家的眼泪

系列文章目录 星际争霸之小霸王之小蜜蜂&#xff08;十三&#xff09;--接着奏乐接着舞 星际争霸之小霸王之小蜜蜂&#xff08;十二&#xff09;--猫有九条命 星际争霸之小霸王之小蜜蜂&#xff08;十一&#xff09;--杀杀杀 星际争霸之小霸王之小蜜蜂&#xff08;十&#xf…

stm32无人机-飞行力学原理

惯性导航&#xff0c;是一种无源导航&#xff0c;不需要向外部辐射或接收信号源&#xff0c;就能自主进行确定自己在什么地方的一种导航方法。 惯性导航主要由惯性器件计算实现&#xff0c;惯性器件包括陀螺仪和加速度计。一般来说&#xff0c;惯性器件与导航物体固连&#xf…

阿里云2核2G服务器e系列租用优惠价格182元性能测评

阿里云服务器经济型e实例2核2G配置优惠价格182.04元一年&#xff0c;系统盘ESSD Entry盘20GB起&#xff0c;公网带宽默认按使用流量&#xff0c;也可以选择按固定带宽计费&#xff0c;带宽值从1M到100M可选&#xff0c;阿腾云分享阿里云服务器2核2G优惠价格、详细配置及e系列CP…

异常记录-VS

1.文件加载失败 无法找到指定路径 Frame GUID: a6c744a8-0e4a-4fc6- 886a-064283054674 Frame mode: VSFM_ MdiChild Error code: 0x80131515 未理会这个提示&#xff0c;可以打开运行项目&#xff0c;只是会跳出这个弹窗。 无法关闭这个异常的窗口。

CorelDraw是什么软件?好用吗

很多人都听过CorelDraw的名字&#xff0c;但不知道CorelDraw是什么样的软件。下面就让小编为大家详细介绍一下。 coreldraw是什么软件 CorelDraw是一款专业的图形设计软件。它的主要功能包括矢量图形和位图的编辑。用户可以利用其矢量图形编辑能力,设计各种图标、Logo等精细图…

《算法竞赛·快冲300题》每日一题:“矩阵”

《算法竞赛快冲300题》将于2024年出版&#xff0c;是《算法竞赛》的辅助练习册。 所有题目放在自建的OJ New Online Judge。 用C/C、Java、Python三种语言给出代码&#xff0c;以中低档题为主&#xff0c;适合入门、进阶。 文章目录 题目描述题解C代码Java代码Python代码 “ 质…

每日已开源的AI论文分享【2023920期】

目录 前言 3D语义场景补全 视频修复 3D人脸重建 视频线条检测 3D物体重建 尾言 前言 作者介绍&#xff1a;作者本人是一名人工智能炼丹师&#xff0c;目前在实验室主要研究的方向为生成式模型&#xff0c;对其它方向也略有了解&#xff0c;希望能够在CSDN这个平台上与同…

【TypeScript】类、类的属性及抽象类(七)

【TypeScript】类、类的属性及抽象类&#xff08;七&#xff09; 【TypeScript】类、类的属性及抽象类&#xff08;七&#xff09;一、前言二、语法及属性定义三、类的继承四、属性类型五、readonly修饰符六、static 静态属性七、存取器-get/set八、抽象类 一、前言 传统的Jav…

定制SQLmap和WAF绕过

1. SQLmap tamper 脚本编写 以sqli-lab第26关为例 输入?id1’ --&#xff0c;报错字符型注入 考虑闭合问题&#xff0c;输入?id1’ and 1&#xff0c;但是回显中and和空格消失了&#xff0c;可知and和空格被过滤了 因为and和or被过滤考虑使用双写绕过手段&#xff0c;空格使…

2023华为杯数学建模D题第三问——区域双碳目标情景设计样例

在第二问建立好预测模型的基础上&#xff0c;如何设计第三问所说的区域双碳路径&#xff0c;以对宏观政策进行指导&#xff01; 采用STIRPA的基本模型对中国碳达峰时间进行预测&#xff0c;对该模型公式两边取对数得到&#xff1a; 其中&#xff1a;P为人口&#xff0c;A为GDP…

NIO圣经:一次穿透NIO、Selector、Epoll底层原理

此pdf电子书&#xff0c;是尼恩架构团队持续升级、持续迭代的作品。 目标是&#xff0c;通过不断升级、持续迭代&#xff0c;为大家构筑一个超底层、超强悍的高性能技术内功。 原 &#xff1a;《九阳真经&#xff1a;彻底明白操作系统 select、epoll 核心原理》 改&#xff1…

2023年研赛华为杯选题人数发布

选题人数发布&#xff01;经过长达30个小时&#xff0c;各个平台的相关选题投票、相关文章阅读量等各项数据进行统计&#xff0c;利用之前的评估办法&#xff08;详见注释&#xff09;。在开赛后30小时&#xff0c;我们基本确定各个赛题选题人数&#xff0c;大致为 题号选题人数…

时间轮算法

思考 假如现在有个任务需要3s后执行&#xff0c;你会如何实现&#xff1f; 线程实现&#xff1a;让线程休眠3s 如果存在大量任务时&#xff0c;每个任务都需要一个单独的线程&#xff0c;那这个方案的消耗是极其巨大的&#xff0c;那么如何实现高效的调度呢&#xff1f; 时…