多路IO转接服务器实现方法三:epoll()函数

2023-05-16

epoll是Linux下多路复用接口select/poll的增强版本。

  1. 它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用每次等待时间之前都必须重新准备要被监听的文件描述符集合
  2. 获取事件的时候,它无须遍历整个被监听的文件描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了
  3. 目前epoll是Linux大规模并发网络程序中的热门首选模型
  4. 除了提供select/poll那种IO事件的电平触发外,还提供了边沿触发,这使得用户空间程序有可能暂存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率

epoll基础API

  • 头文件<sys/epoll.h>

1 epoll_create函数

函数原型

int epoll_create(int size);

  • 功能
    创建一个epoll句柄,参数size用来告诉内核监听的文件描述符的个数,跟内存大小有关。
  • 参数
    size:监听数目

2 epoll_ctl函数

函数原型

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

  • 功能
    控制某个epoll监控的文件描述符上的事件:注册、修改、删除。

  • 参数
    epfd:为epoll_create的句柄
    event:告诉内核需要监听的事件
    op:表示动作,常用的三个宏

    • EPOLL_CTL_ADD:注册新的fd到epfd
    • EPOLL_CTL_MOD:修改已经注册的fd的监听事件
    • EPOLL_CTL_DEL:从epfd删除一个fd
  • 两个结构体

    struct epoll_event{
    	_uint32_t events;
    	epoll_data_t data;
    }
    struct union epoll_data{
    	void* ptr;
    	int fd;
    	uint32_t u32;
    	uint64_t u64;
    }epoll_data_t;
    

3 epoll_wait函数

函数原型

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

  • 功能
    等待所监控文件描述符上有事件的产生,类似于select()调用
  • 参数
    events:用来存内核得到事件的集合
    maxevents:告诉内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size
    timeout:超时时间
    • -1:阻塞
    • 0:立即返回,非阻塞
    • 0:指定毫秒

  • 返回值
    成功返回有多少满足条件的文件描述符。
    超时时间到时返回0,出错返回-1.

例子

#include <stdio.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define IP "127.0.0.1"
#define PORT 8888

int main()
{
    //1、创建套接字
    int sfd = socket(AF_INET,SOCK_STREAM,0);

    //设置端口复用,免去TIME_WAIT阶段需要等的2MSL时间
    int opt = 1;
    setsockopt(sfd,SOL_SOCKET,SO_REUSEPORT,&opt,sizeof (opt));

    //2、绑定
    struct sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    inet_pton(AF_INET,IP,&serv_addr.sin_addr.s_addr);
    socklen_t serv_len = sizeof (serv_addr);
    int ret = bind(sfd,(struct sockaddr*)&serv_addr,serv_len);
    if(ret != 0){
        printf("bind error:%s\n",strerror(ret));
        exit(1);
    }

    //3、监听,设置最大连接数
    listen(sfd,128);

    //开始使用epoll帮忙监听
    while(1){
        //创建红黑树的根节点出来,返回值epfd个数是要监听的文件描述符个数
        int epfd = epoll_create(10);
        if(ret < 0){
            perror("epoll_create error");
            exit(2);
        }

        //将sfd文件描述符挂到这颗红黑树上
        struct epoll_event inevent;
        inevent.events = EPOLLIN;
        inevent.data.fd = sfd;
        epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&inevent);

        while(1){
            //监听,如果有事件发生,会传出到outevent中
            struct epoll_event outevent[10];
            ret = epoll_wait(epfd,outevent,sizeof (outevent),-1);
            //epoll_wait函数发生后,就会有事件发生后,直接遍历这ret个文件描述符,看是哪个文件描述符发生了事件
            for(int i = 0; i < ret;i++){
                printf("-----------%d------------\n",i);
                if(!(outevent[i].events & EPOLLIN))//如果不是“读”事件就continue
                    continue;

                //判断如果是sfd有读事件发生,说明有新的客户端连接,调用accept
                //如果不是sfd有读事件发生,说明是客户端要发送数据
                int sockfd = outevent[i].data.fd;
                if(outevent[i].data.fd == sfd){
                    struct sockaddr_in clie_addr;
                    socklen_t clie_len = sizeof (clie_addr);
                    sockfd = accept(sfd,(struct sockaddr*)&clie_addr,&clie_len);
                    //打印客户端的ip和port
                    char buf[BUFSIZ];
                    memset(buf,0,sizeof (buf));
                    printf("%s connected, port: %d\n",inet_ntop(AF_INET,&clie_addr.sin_addr.s_addr,buf,
                                                                sizeof (buf)),ntohs(clie_addr.sin_port));

                    //将新连接的客户端的文件描述符也加入到这颗红黑树中进行监听
                    struct epoll_event cevent;
                    cevent.events = EPOLLIN;
                    cevent.data.fd = sockfd;
                    epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&cevent);
                }else{
                    char rwbuf[BUFSIZ];
                    memset(rwbuf,0,sizeof (rwbuf));
                    int len = read(sockfd,rwbuf,sizeof (rwbuf));
                    if(len == 0){//读结束了,客户端关闭了
                        printf(" client disconnected\n");
                        //将这个文件描述符从监听列表中去除
                        close(sockfd);
                        epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,outevent);
                    }else if(len > 0){
                        for(int j = 0; j < len; j++){
                            rwbuf[j] = toupper(rwbuf[j]);
                        }
                        write(sockfd,rwbuf,len);
                    }
                }
            }


        }
    }
    close(sfd);
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多路IO转接服务器实现方法三:epoll()函数 的相关文章

  • epoll源码解析翻译------说使用了mmap的都是骗子

    本文地址 https www cnblogs com l2017 p 10830391 html https blog csdn net li haoren select poll epoll这三个都是对poll机制的封装 只是select
  • Epoll两种模式浅析(ET or LT)

    linux异步IO浅析 http hi baidu com kouu blog item e225f67b337841f42f73b341 html epoll有两种模式 Edge Triggered 简称ET 和 Level Trigge
  • c语言用epoll实现ftp服务器

    epoll简介 xff1a https www cnblogs com Anker archive 2013 08 17 3263780 html 以下是我根据上面这篇epoll例子改写的实现ftp服务器代码 xff1a server c
  • 从redis为什么单线程还那么快到epoll的设计原理

    文章目录 redis为什么快 上下文切换 为什么采用单线程 redis的I O多路复用 epoll与select poll区别 select poll的几大缺点 用户态拷贝到内核态 epoll IO多路复用模型实现机制 epoll 优势详解
  • Janus网关的集成与优化

    本文由学霸君音视频架构师李桥平在LiveVideoStackCon 2020 线上峰会的演讲内容整理而成 内容主要包括Janus网关的集成过程以及集成过程中遇到的一些问题 文 李桥平 整理 LiveVideoStack 大家好 我是李桥平
  • Epoll事件ET和LT模型分析

    1 Epoll事件有两种模型 ET 边沿触发 缓冲区状态发生变化时 触发一次 LT 水平触发 有数据可读 读事件一直触发 有空间可写 写事件一直触发 使用时 不指定事件模型 则默认是水平触发 2 ET模型 ET边缘触发模型 涉及以下问题 1
  • linux内核epoll实现分析

    为了更好的分享体验 博客搬迁至极客驿站 欢迎查阅 epoll与select poll的区别 select poll epoll都是IO多路复用的机制 I O多路复用就通过一种机制 可以监视多个描述符 一旦某个描述符就绪 能够通知程序进行相应
  • PyWebIo

    Part1什么是 PyWebIo PyWebIO 提供了一系列命令式的交互函数来在浏览器上获取用户输入和进行输出 将浏览器变成了一个 富文本终端 可以用于构建简单的 Web 应用或基于浏览器的 GUI 应用 使用 PyWebIO 开发者能像
  • 从零编写c++之http服务器(3)-http服务

    http全称超文本传输协议 可调试性高 扩展性也强 上两个篇章我们已经拥有了epoll事件驱动框架和线程池处理网络事件 接下来我们要先写一个基础网络套接字 然后在此基础上扩展出http的套接字 献上类图如下 完整源码见
  • C/C++实现协程及原理(详细完整版)-架构师篇

    一 协程 Coroutine 简介 协程 又称微线程 纤程 英文名Coroutine 协程的概念很早就提出来了 但直到最近几年才在某些语言 如Lua 中得到广泛应用 子程序 或者称为函数 在所有语言中都是层级调用 比如A调用B B在执行过程
  • 面试官:说说Node中的EventEmitter? 如何实现一个EventEmitter?

    一 是什么 我们了解到 Node采用了事件驱动机制 而EventEmitter就是Node实现事件驱动的基础 在EventEmitter的基础上 Node几乎所有的模块都继承了这个类 这些模块拥有了自己的事件 可以绑定 触发监听器 实现了异
  • 网络编程14——epoll反应堆模型⭐,epoll反应堆实现源码(并没掌握▼

    epoll ET模式 非阻塞 void ptr epoll反应堆模式 与原来监听模式对比 给lfd和cfd指定回调函数的区别 epoll反应堆实现源码 这代码有点难 eventset函数 设置回调函数 lfd gt acceptconn c
  • epoll移植到windows的可行性研究

    1 各有千秋 linux作为高效稳定的操作系统 部署在大量的服务器上 epoll在linux下 一个高性能的网络IO模型 在服务端领域发挥着重要的作用 但在开发效率上 windows以及visual studio系列因其良好的用户体验而更受
  • 边缘触发的 epoll 和 oneshot 只报告一次

    我目前正在添加从创建的 sockfdsaccept到具有以下事件的 epoll 实例 const int EVENTS EPOLLET EPOLLIN EPOLLRDHUP EPOLLONESHOT EPOLLERR EPOLLHUP 一旦
  • 你能推荐一些关于 Linux 上的 Epoll 的指南吗?

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我需要了解 Linux 系统上的 Epoll 您能推荐有关 epoll 库的手册或指南吗 需要更详细的指南 最好有一些例子 帮我 感谢您的
  • 有没有办法在 asyncio 构建的预分叉多进程 TCP 服务器上进行负载平衡?

    感谢之前回答的人 现在我可以构建一个多进程 TCP 服务器 每个进程分别运行一个异步服务器 但全部绑定到一个端口 使用 asyncio 时无法使用 os fork 将多个进程绑定到一个套接字服务器 https stackoverflow c
  • epoll 与 select 对于极少量的连接

    我一直使用 select 来处理连接 最近我们的套接字库发生了变化 select 被 Linux 平台的 epoll 取代 我的应用程序架构是这样的 我只建立一个或最多 2 个套接字连接 并在单个线程中对它们进行 epoll select
  • epoll_wait()接收socket关闭两次(read()/recv()返回0)

    我们有一个使用 epoll 来侦听和处理 http 连接的应用程序 有时 epoll wait 会连续两次收到 fd 上的 close 事件 含义 epoll wait 返回连接 fd 其中 read recv 返回 0 这是一个问题 因为
  • 如何将 boost::asio 与 Linux GPIO 结合使用

    我有一个单线程 Linux 应用程序 使用 boost asio 进行异步输入 输出 现在我需要扩展此应用程序以读取 GPIO 输入 sys class gpio gpioXX value 可以在边沿触发的 GPIO 输入上使用 boost
  • TCL类C10K事件服务器开发进展如何?

    TCL 是一种很好的简单编程语言 但似乎没有得到认可和 或尊重它deserves http antirez com articoli tclmisunderstood html 我 1995 年在大学时就学过它 但很快就忘记了 直到最近才再

随机推荐