Linux IO多路复用之epoll网络编程

Linux IO多路复用之epoll网络编程攻略

什么是IO多路复用

IO多路复用是一种异步I/O模型,允许单个进程同时监控多个文件描述符,当某个文件描述符发生IO事件时,可以及时地通知进程进行处理,提高系统的资源利用率和IO效率。

在Linux中,IO多路复用主要有三种实现方法:select、poll和epoll。其中,epoll是Linux2.6内核中引入的新机制,性能更优,使用范围更广。

epoll的工作原理

epoll的工作原理与select和poll类似,都是通过监听文件描述符集合来实现IO多路复用。但是,epoll具有以下几个优点:

  1. 基于事件驱动,只有在IO事件发生时才会通知进程,而不是像select和poll那样需要轮询。
  2. 支持边缘触发和水平触发两种模式,能够更精细地控制IO事件的处理。
  3. 支持基于文件描述符的事件注册方式,不需要每次都要重复注册和复制文件描述符集合。

epoll的使用步骤

使用epoll实现IO多路复用需要完成以下几个步骤:

  1. 调用epoll_create创建一个epoll对象。
  2. 定义一个事件结构体epoll_event,用于存储每个事件的详细信息。
  3. 调用epoll_ctl向epoll对象中添加、删除或修改文件描述符的事件。
  4. 调用epoll_wait等待IO事件的发生。
  5. 处理IO事件。

epoll示例

下面给出两个epoll的示例,一个是TCP服务器程序示例,另一个是UDP客户端和服务器程序示例。

TCP服务器程序示例

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

#define MAX_EVENTS 1024
#define PORT 8888

int main() {
    int listenfd, connfd, epollfd, nfds, i;
    char buf[1024];
    struct sockaddr_in servaddr, cliaddr;
    socklen_t clilen = sizeof(cliaddr);
    struct epoll_event ev, events[MAX_EVENTS];

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0) {
        perror("socket error");
        return -1;
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);

    if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind error");
        return -1;
    }

    if(listen(listenfd, 5) < 0) {
        perror("listen error");
        return -1;
    }

    epollfd = epoll_create(MAX_EVENTS);
    if (epollfd < 0) {
        perror("epoll_create error");
        return -1;
    }

    ev.events = EPOLLIN | EPOLLET;
    ev.data.fd = listenfd;

    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev) < 0) {
        perror("epoll_ctl error");
        return -1;
    }

    while (1) {
        nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if (nfds < 0) {
            perror("epoll_wait error");
            continue;
        }

        for (i = 0; i < nfds; i++) {
            if (events[i].data.fd == listenfd) {
                connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
                if (connfd < 0) {
                    perror("accept error");
                    continue;
                }

                ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = connfd;

                if (epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev) < 0) {
                    perror("epoll_ctl error");
                    return -1;
                }
            } else if (events[i].events & EPOLLIN) {
                connfd = events[i].data.fd;
                int n = read(connfd, buf, sizeof(buf));
                if (n < 0) {
                    perror("read error");
                    epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &ev);
                    close(connfd);
                    continue;
                } else if (n == 0) {
                    epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &ev);
                    close(connfd);
                    continue;
                }

                write(STDOUT_FILENO, buf, n);

                ev.events = EPOLLOUT | EPOLLET;
                ev.data.fd = connfd;

                if (epoll_ctl(epollfd, EPOLL_CTL_MOD, connfd, &ev) < 0) {
                    perror("epoll_ctl error");
                    return -1;
                }
            } else if (events[i].events & EPOLLOUT) {
                connfd = events[i].data.fd;
                sprintf(buf, "HTTP/1.1 200 OK\r\nContent-Length: 14\r\n\r\nHello, World!\n");
                int n = write(connfd, buf, strlen(buf));
                if (n < 0) {
                    perror("write error");
                    epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &ev);
                    close(connfd);
                    continue;
                }

                ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = connfd;

                if (epoll_ctl(epollfd, EPOLL_CTL_MOD, connfd, &ev) < 0) {
                    perror("epoll_ctl error");
                    return -1;
                }
            }
        }
    }
    return 0;
}

UDP客户端和服务器程序示例

// UDP客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>

#define MAX_EVENTS 1024
#define SERVER_ADDR "127.0.0.1"
#define SERVER_PORT 8888

int main() {
    int sockfd, res, epollfd, nfds, i;
    char buf[1024];
    struct sockaddr_in servaddr;
    struct epoll_event ev, events[MAX_EVENTS];

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket error");
        return -1;
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
    servaddr.sin_port = htons(SERVER_PORT);

    epollfd = epoll_create(MAX_EVENTS);
    if (epollfd < 0) {
        perror("epoll_create error");
        return -1;
    }

    ev.events = EPOLLIN | EPOLLET;
    ev.data.fd = sockfd;

    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev) < 0) {
        perror("epoll_ctl error");
        return -1;
    }

    while (1) {
        nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if (nfds < 0) {
            perror("epoll_wait error");
            continue;
        }

        for (i = 0; i < nfds; i++) {
            if (events[i].data.fd == sockfd) {
                fgets(buf, sizeof(buf), stdin);
                sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
            } else if (events[i].events & EPOLLIN) {
                res = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
                buf[res] = '\0';
                printf("%s", buf);
            }
        }
    }
    return 0;
}

// UDP服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>

#define BUF_LEN 1024
#define SERVER_PORT 8888
#define MAX_EVENTS 1024

int main() {
    int sockfd, res, epollfd, nfds, i;
    char buf[BUF_LEN];
    struct sockaddr_in servaddr, cliaddr;
    struct epoll_event ev, events[MAX_EVENTS];
    socklen_t clilen = sizeof(cliaddr);

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket error");
        return -1;
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERVER_PORT);

    if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind error");
        return -1;
    }

    epollfd = epoll_create(MAX_EVENTS);
    if (epollfd < 0) {
        perror("epoll_create error");
        return -1;
    }

    ev.events = EPOLLIN | EPOLLET;
    ev.data.fd = sockfd;

    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev) < 0) {
        perror("epoll_ctl error");
        return -1;
    }

    while (1) {
        nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
        if (nfds < 0) {
            perror("epoll_wait error");
            continue;
        }

        for (i = 0; i < nfds; i++) {
            if (events[i].events & EPOLLIN) {
                res = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&cliaddr, &clilen);
                buf[res] = '\0';
                printf("Received from client %s:%d: %s", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port, buf);

                sendto(sockfd, buf, res, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
            }
        }
    }
    return 0;
}

以上就是使用epoll实现IO多路复用的完整攻略以及两个示例的演示代码。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Linux IO多路复用之epoll网络编程 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • 解决springjpa的局部更新字段问题

    下面我来详细讲解“解决springjpa的局部更新字段问题”的完整攻略。 1. 问题描述 在使用springjpa进行数据库操作的时候,有时会遇到要更新某个实体类的部分字段,而不是全部字段。但是springjpa默认的update操作只能更新整个实体类的所有字段,无法实现局部更新。 2. 解决方案 解决这个问题的方案有两种: 2.1 使用@Query注解 可…

    other 2023年6月25日
    00
  • Win10锁屏状态怎么设置打开任意应用程序?

    下面是详细讲解Win10锁屏状态如何设置打开任意应用程序的完整攻略: 1. 打开“组策略编辑器”工具 按下“Win+R”键打开“运行”窗口,输入“gpedit.msc”,并点击“确定”按钮打开“组策略编辑器”工具。 2. 找到“计算机配置”中的“Windows 设置” 在“组策略编辑器”窗口的左侧栏中,依次展开“计算机配置”、“管理模板”、“Windows …

    other 2023年6月25日
    00
  • Flutter 网络请求框架封装详解

    Flutter 网络请求框架封装详解 网络请求是移动应用中常用的功能,Flutter提供了丰富的网络请求支持和第三方库,如http、dio等。为了简化开发流程,最好将网络请求进行封装。 封装思路 封装网络请求的主要思路是将网络请求的参数进行封装,提高代码复用率和可读性。一般封装网络请求都会包含以下几个步骤: 封装请求参数和请求路径 封装请求头 封装请求体 封…

    other 2023年6月25日
    00
  • jetbrainsc++ideclion配置与评测

    JetBrains C++ IDE CLion配置与评测 JetBrains C++ IDE CLion是一款功能强大的C++开发工具,它提供了丰富的功能和工具,可以帮助我们更高效地开发C++应用程序。以下是JetBrains C++ IDE CLion配置与评测的完整攻略。 步骤 以下是JetBrains C++ IDE CLion配置与评测的步骤: 下载…

    other 2023年5月6日
    00
  • FlareGet Download Manager怎么激活 附下载地址+激活补丁

    FlareGet Download Manager激活攻略 FlareGet Download Manager是一款功能强大的下载管理器,可以提供更快速、稳定的下载体验。以下是激活FlareGet Download Manager的完整攻略,包括下载地址和激活补丁。 下载FlareGet Download Manager 首先,你需要下载FlareGet D…

    other 2023年8月4日
    00
  • 手机空间不足怎么办 手机空间不足的解决办法

    手机空间不足的解决办法 手机空间不足是一个常见的问题,但是有很多方法可以解决这个问题。下面是一些解决手机空间不足问题的方法和示例说明: 1. 清理手机内存 清理手机内存是解决手机空间不足问题的最基本方法之一。以下是一些可以清理手机内存的方法: 删除不需要的应用程序:检查手机上安装的应用程序,并删除不再使用或不需要的应用程序。这将释放一些存储空间。例如,你可能…

    other 2023年7月31日
    00
  • SQL Server Reporting Services 匿名登录的问题及解决方案

    实现SSRS匿名登录是一项常见的需求,但它涉及到一些默认安全限制,因此需要掌握一些技巧来处理。本文将详细讲解匿名登录的问题及解决方案。 1. 问题描述 在默认情况下,SSRS报表需要认证用户才能访问报表,这意味着,无论在本地还是在远程环境中,用户必须提供正确的凭据才能访问报表。而有些情况下,我们希望用户能够匿名访问报表而无需提供凭据。如果您尝试访问报表服务器…

    other 2023年6月26日
    00
  • Red Hat Linux 安全设置方法

    Red Hat Linux 安全设置方法 本文将详细讲解如何在 Red Hat Linux 操作系统中进行安全设置,主要包括以下内容: 关闭不必要的服务 安装防火墙并配置规则 更新系统补丁 利用 SELinux 增强安全 设置强密码和用户权限 实施访问控制 1. 关闭不必要的服务 首先,我们应该关闭不必要的服务,以减少攻击面和提高系统性能。可以通过以下命令查…

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