epool介绍

epoll介绍

epoll是Linux内核提供的一种高效的I/O多路复用机制,用于处理大量的并发连接。它可以监视多个文件描述符,当其中任何一个文件描述符就绪时,就通知应用程序进行处理。ep是Linux内核2.6版本引入的,相比于select和poll,它具有更好的性能和可伸缩性。

epoll的优点

  • 支持较大的并发连接数,可以处理数百万个连接。
  • 监视的文件描述数量不受限制,可以支持数百万个文件描述符。
  • 支持水平触发和边缘触发两种模式。
  • 支持EPOLLONESHOT选项,可以保证每个文件描述符只被一个程处理。
  • 支持EPOLLEXCLUSIVE选项,可以保证每个文件描述符只被一个进程处理。

epoll的工作原理

epoll使用一个事件表来存储文件描述符和事件。当调用epoll_wait()函数时,内核会检查事件表中的文件描述符,如果有文件描述符就绪,就将其加入到就绪链表中。epoll_wait()函数会返回就绪链表中的文件描述符数量,并将就绪链表中的文件描述符复制到用户空间中。应用程序可以使用这些文件描述符进行读写操作。

示例一:使用epoll实现高并发服务器

#include <sysepoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define MAX_EVENTS 1024
#define BUF_SIZE 1024

int setnonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) {
        return -1;
    }
    flags |= O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flags) == -1) {
        return -1;
    }
    return 0;
}

int main(int argc, char *argv[]) {
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(8080);

    if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    if (listen(listen_fd, SOMAXCONN) == -1) {
       ("listen");
        exit(EXIT_FAILURE);
    }

    int epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    struct epoll_event event;
    event.data.fd = listen_fd;
    event.events = EPOLLIN | EPOLLET;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) == -1) {
        perror("epoll_ctl");
        exit(EXIT_FAILURE    }

    struct epoll_event *events = (struct epoll_event *)calloc(MAX_EVENTS, sizeof(struct epoll_event));

    while (1) {
        int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        for (int i = 0; i <; i++) {
            if (events[i].data.fd == listen_fd) {
                int conn_fd = accept(listen_fd, NULL, NULL);
                if (conn_fd == -1) {
                    perror("accept");
                    continue;
                }
                setnonblocking(conn_fd);
                event.data.fd = conn_fd;
                event.events = EPOLLIN | EPOLLET;
                if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &event) == -1) {
                    perror("epoll_ctl");
                    exit(EXIT_FAILURE);
                }
            } else {
                int conn_fd = events[i].data.fd;
                char buf[BUF_SIZE];
                memset(buf, 0, sizeof(buf));
                int n = read(conn_fd, buf, sizeof(buf));
                if (n == -1) {
                    if (errno == ECONNRESET) {
                        close(conn_fd);
                        epoll_ctl(epoll_fd, EPOLL_CTL_DEL, conn_fd, NULL);
                    } else {
                        perror("read");
                    }
                } else if (n == 0) {
                    close(conn_fd);
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, conn_fd, NULL);
                } else {
                    printf("recv: %s\n", buf);
                    write(conn_fd, buf, n);
                }
            }
        }
    }

    free(events);
    close(epoll_fd);
    close(listen_fd);

    return 0;
}

在这个示例中,我们使用epoll实现了一个高并发的服务器。首先创建一个socket并绑定到指定的端口,然后使用epoll_create1()函数创建一个epoll实例。将监听socket添加到epoll实例中,然后使用epoll()函数等待事件。当有新的连接到来时,将连接socket添加到epoll实例中。当连接socket可读时,读取数据并将其发送回客户端。

示例二:使用epoll实现高性能的HTTP服务器

#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define MAX_EVENTS 1024
#define BUF_SIZE 1024

int setnonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) {
        return -1;
    }
    flags |= O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flags) == -1) {
        return -1;
    }
    return 0;
}

int main(int argc, char *argv[]) {
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(8080);

    if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    if (listen(listen_fd, SOMAXCONN) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    int epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    struct epoll_event event;
    event.data.fd = listen_fd;
    event.events = EPOLLIN | EPOLLET;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) == -1) {
        perror("epoll_ctl");
        exit(EXIT_FAILURE);
    }

    struct epoll_event *events = (struct epoll_event *)calloc(MAX_EVENTS, sizeof(struct epoll_event));

    while (1) {
        int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        for (int i = 0; i < n; i++) {
            if (events[i].data.fd == listen_fd) {
                int conn_fd = accept(listen_fd, NULL, NULL);
                if (conn_fd == -1) {
                    perror("accept");
                    continue;
                }
                setnonblocking(conn_fd);
                event.data.fd = conn_fd;
                event.events = EPOLLIN | EPOLLET;
                if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &event) == -1) {
                    perror("epoll_ctl");
                    exit(EXIT_FAILURE);
                }
            } else {
                int conn_fd = events[i].data.fd;
                char buf[BUF_SIZE];
                memset(buf, 0, sizeof(buf));
                int n = read(conn_fd, buf, sizeof(buf));
                if (n == -1) {
                    if (errno == ECONNRESET) {
                        close(conn_fd);
                        epoll_ctl(epoll_fd, EPOLL_CTL_DEL, conn_fd, NULL);
                    } else {
                        perror("read");
                    }
                } else if (n == 0) {
                    close(conn_fd);
                    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, conn_fd, NULL);
                } else {
                    char response[BUF_SIZE];
                    memset(response, 0, sizeof(response));
                    sprintf(response, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s", n, buf);
                    write(conn_fd, response, strlen(response));
                }
            }
        }
    }

    free(events);
    close(epoll_fd);
    close(listen_fd);

    return 0;
}

在这个示例中,我们使用epoll实现了一个高性能的HTTP服务器。与前面的示例类似,首先创建一个socket并绑定到指定的端口,然后使用epoll_create1()函数创建一个epoll实例。将监听socket添加到epoll实例中,然后使用epoll_wait()函数等待事件。当有新的连接到来时,将连接socket添加到epoll实例中。当连接socket可读时,读取数据并将其作为HTTP响应发送回客户端。

总之,epoll是Linux内核提供的一种高效的I/O多路复用机制,用于处理大量的并发连接。可以使用epoll实现高并发服务器或高性能的HTTP服务器。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:epool介绍 - Python技术站

(0)
上一篇 2023年5月8日
下一篇 2023年5月8日

相关文章

  • nginx中文件下载指定保存文件名的配置方法

    在nginx服务器上,可以通过配置来控制文件下载时写入到用户本地保存的文件名。具体的配置方法如下: 在nginx.conf文件中,找到http部分,并在其中添加以下代码块: http { … include ./mime.types; default_type application/octet-stream; } 找到server部分,并在其中添加以下…

    other 2023年6月26日
    00
  • Mybatis-Plus实现公共字段自动赋值的方法

    下面是关于”Mybatis-Plus实现公共字段自动赋值的方法”的详细讲解: 什么是公共字段自动赋值 在许多实际的应用场景中,我们需要在插入或者更新数据时自动赋值某些公共字段,比如创建时间、更新时间、创建人、更新人等信息。这些公共字段信息通常是由系统自动维护,并且在插入或更新时需要自动赋值,而不是由用户手动设置。Mybatis-Plus提供了一个非常方便的方…

    other 2023年6月27日
    00
  • 详解React项目的服务端渲染改造(koa2+webpack3.11)

    详解React项目的服务端渲染改造(koa2+webpack3.11) 1. 概述 本文将介绍如何将一个React项目改造成服务端渲染的形式,并使用Koa2和webpack3.11完成。 服务端渲染的好处是能够提高网站的SEO和首屏渲染速度,并且能够更好地应对一些搜索引擎不友好的单页面应用(SPA)。通过本文,你将掌握如何在一个React项目中加入服务端渲染…

    other 2023年6月27日
    00
  • C++内存池两种方案解析

    C++内存池两种方案解析 什么是内存池 内存池是一种特殊的内存管理机制,它在程序启动时分配一段连续的内存空间,然后根据客户端的需求,在内存池中分配一定大小的内存。内存池中的内存不是实时分配和释放,而是在一开始就将需要使用的内存一并分配好,然后再慢慢的释放。 内存池的优点有: 减轻内存碎片问题; 提高了内存使用效率; 减少了内存动态分配的次数; 减少了程序运行…

    other 2023年6月27日
    00
  • 电脑下载的软件不在桌面显示怎么办 解决安装后的软件不在桌面问题

    问题描述:当我们在电脑上安装了新的软件或者游戏时,有时候我们会发现在安装完成后,这些软件或者游戏并没有出现在桌面上,这种情况该如何解决呢? 解决方法:通常情况下,当我们安装软件时,会询问安装路径,我们要安装到哪个文件夹下。有的软件默认安装在 C 盘,有时候我们可以找到安装文件夹里的 .exe 文件,直接运行软件。但是如果我们按照默认的方式安装,在桌面上就无法…

    other 2023年6月27日
    00
  • centos6下docker的安装和使用

    以下是CentOS 6下Docker的安装和使用的完整攻略,包括两个示例说明。 1. Docker的安装 在CentOS 6下安装Docker,可以按照以下步骤进行: 安装必要的依赖包: sudo yum install -y yum-utils device-mapper-persistent-data lvm2 添加Docker的yum源: sudo y…

    other 2023年5月9日
    00
  • wmplayer

    以下是详细讲解“wmplayer的完整攻略”的标准Markdown格式文本: wmplayer的完整攻略 Windows Media Player(wmplayer)是一款由微软公司开发的多媒体播放器可以播放音频、视频和图像等多种格式的文件。本文将介绍wmplayer的完整攻略,包括wmplayer的基本念、wmplayer的应用场景和两个示例说明。 1. …

    other 2023年5月10日
    00
  • 一看就懂的Android APP开发入门教程

    一看就懂的Android APP开发入门教程 简介 本教程旨在帮助初学者快速入门Android APP开发。我们将使用Java语言和Android Studio开发环境进行开发。在本教程中,我们将学习如何创建一个简单的计算器应用程序。 步骤 步骤1:设置开发环境 首先,我们需要安装Java JDK和Android Studio。请按照以下步骤进行设置: 下载…

    other 2023年7月27日
    00
合作推广
合作推广
分享本页
返回顶部