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日

相关文章

  • React+Electron快速创建并打包成桌面应用的实例代码

    我将在以下内容中详细讲解 “React+Electron快速创建并打包成桌面应用的实例代码”的完整攻略。 简介 React 和 Electron 分别是前端和桌面开发中常用的工具。React 是一个基于 JavaScript 的图形 UI 库,它可以高效地构建 Web 应用程序的用户界面。Electron 是一个基于 Chromium 和 Node.js 实…

    other 2023年6月27日
    00
  • 故事讲解Activity生命周期(猫的一生)

    故事讲解Activity生命周期(猫的一生)是一种有趣且易于理解的方式,用于说明Android应用程序中Activity的生命周期,以下是完整攻略: 1. 故事简介 一只小猫出生了,它刚开始很活跃,充满了活力。它会玩耍、会吃饭、会跳舞,这个过程就相当于Activity的生命周期。当小猫被主人带到其他场合时,它需要适应不同的环境,这个时候就相当于Activit…

    other 2023年6月27日
    00
  • sql中 order by 和 group by的区别

    让我们来讲解一下“SQL中ORDER BY和GROUP BY的区别”: ORDER BY ORDER BY 是用于排序结果集的关键字。它将排序结果按照指定的列或表达式进行排序,可以使用 ASC (升序)或 DESC (降序)来指定排序方向,默认为升序。 下面是一些示例,说明 ORDER BY 是如何工作的。 示例1 我们使用下面的 SQL 语句查询一个表中的…

    other 2023年6月25日
    00
  • 适用于linux的7种最佳notepad++替代品

    以下是关于“适用于Linux的7种最佳Notepad++替代品”的完整攻略,过程中包含两个示例。 背景 Notepad++是一款行的文本编辑器,它提供了许多有用的功能,如语法高亮、动、宏录等。但是,Not++只能在Windows操作系统上运行。对于Linux用户,我们需要寻找其他的文本编辑器来代Notepad++。本攻略将介绍适用于Linux的7种最佳Not…

    other 2023年5月9日
    00
  • UDP简单服务端客户端代码示例

    UDP简单服务端客户端代码示例分为两个部分:服务端和客户端。服务端基于UDP协议收发数据,客户端向服务端发送数据并接收服务端的响应。下面详细讲解编写UDP简单服务端客户端代码的步骤。 编写UDP服务端代码 创建UDP Socket import socket server_socket = socket.socket(socket.AF_INET, sock…

    other 2023年6月27日
    00
  • vue报错”vue-cli-service‘不是内部或外部命令,也不是…”的解决办法

    当我们使用Vue CLI 4.x版本创建项目时,有时候会出现“vue-cli-service‘不是内部或外部命令,也不是可运行的程序”这个错误提示。这个问题通常是因为npm或yarn没有正确安装Vue CLI的命令行工具导致的。 以下是详细的解决步骤: 第一步:检查Vue CLI的版本 为了解决这个问题,我们首先需要检查我们安装的Vue CLI版本是否正确。…

    other 2023年6月27日
    00
  • java格式化数字操作 NumberFormat及DecimalFormat

    让我为您讲解一下“java格式化数字操作 NumberFormat及DecimalFormat”的攻略。 1. NumberFormat及DecimalFormat简介 NumberFormat是java.util包中的一个抽象类,是将数字格式化为字符串的基类,它提供了很多方法来格式化数字,例如将数字格式化为货币、百分数等。 DecimalFormat是Nu…

    other 2023年6月26日
    00
  • Java:”失效”的private修饰符

    关于Java中”失效”的private修饰符,其实是指在某些情况下,private修饰符并不能有效地限制外部访问类的私有成员或方法。接下来,我将为大家提供一个完整的攻略,来帮助深入理解如何有效使用private修饰符。 1. private修饰符的定义 首先,我们需要明确private修饰符的作用和定义。private修饰符是Java中4种访问修饰符之一,用…

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