epoll机制

1、创建epoll例程

       #include <sys/epoll.h>

       int epoll_create(int size);
        // 成功时返回epoll文件描述符,失败时返回-1

调用epoll_create函数时创建的监视对象文件描述符保存空间称为“epoll例程”。

参数size并非用来决定epoll例程的大小,而仅供操作系统参考。该函数返回的文件描述符主要用于区分epoll例程。

2、向epoll例程内部注册监视对象文件描述符

调用epoll_ctl函数向epoll例程内部注册监视对象文件描述符。

       #include <sys/epoll.h>

       int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
        //成功时返回0, 失败时返回-1

参数epfd 表示向文件描述符epfd所指代的epoll例程中注册监视对象。

参数op 用于指定监视对象的添加、删除或更改等操作

参数fd 需要注册的监视对象的文件描述符

参数event 监视对象的事件类型。

参数event是结构体epoll_event类型的指针,结构体epoll_event的声明如下所示:

struct epoll_event
{
    __uint32_t events;
    epoll_data_t data;
}

该结构体的成员data用于注册监视对象文件描述符,成员events用于注册关注的事件,事件可以是EPOLLIN(需要读取数据的事件)、EPOLLOUT(可以立即发送数据的事件)等。

3、等待事件的发生

调用epoll_wait函数等待监视对象发生变化。

       #include <sys/epoll.h>

       int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
        // 成功时返回发生事件的文件描述符数量,失败时返回-1

参数epfd 表示事件发生监视范围的epoll例程的文件描述符。

参数events 用于记录发生事件的文件描述符的结构体epoll_event型数组的地址。

参数maxevents 第二个参数可以保存的最大事件数。

参数timeout 以1/1000秒为单位的等待时间,传递-1时,表示一直等待直到发生事件。

第二个参数所指缓冲需要使用malloc函数动态分配。

调用函数后,返回发生事件的文件描述符数,同时将记录发生事件的文件描述符的结构体epoll_event保存在第二个参数指向的缓冲中

4、epoll机制服务器实例

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

#define BUF_SIZE 100
#define EPOLL_SIZE 50
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int serv_sock, clnt_sock;
    struct sockaddr_in serv_adr, clnt_adr;
    socklen_t adr_sz;
    int str_len, i;
    char buf[BUF_SIZE];

    struct epoll_event *ep_events;
    struct epoll_event event;
    int epfd, event_cnt;

    if (argc != 2)
    {
        printf("Usage : %s <port> \n", argv[0]);
        exit(1);
    }
    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_adr.sin_port = htons(atoi(argv[1]));

    if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
        error_handling("bind() error");
    if (listen(serv_sock, 5) == -1)
        error_handling("listen() error");
    // epoll_create函数创建的文件描述符保存空间称为“epoll例程”,该函数返回的文件描述符主要用于区分epoll例程。
    epfd = epoll_create(EPOLL_SIZE); //可以忽略这个参数,填入的参数为操作系统参考
    // 创建用于记录发生事件的文件描述符的epoll_event型数组
    ep_events = malloc(sizeof(struct epoll_event) * EPOLL_SIZE); 

    event.events = EPOLLIN; //需要读取数据的情况
    event.data.fd = serv_sock;
    epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event); //向例程epfd 中注册文件描述符 serv_sock,目的是监听 event 中的事件

    while (1)
    {
        event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1); //获取改变了的文件描述符,返回数量
        if (event_cnt == -1)
        {
            puts("epoll_wait() error");
            break;
        }

        for (i = 0; i < event_cnt; i++)
        {
            if (ep_events[i].data.fd == serv_sock) //客户端请求连接时
            {
                adr_sz = sizeof(clnt_adr);
                clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz);
                event.events = EPOLLIN;
                event.data.fd = clnt_sock; //把客户端套接字添加进去
                epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);
                printf("connected client : %d \n", clnt_sock);
            }
            else //是客户端套接字时
            {
                str_len = read(ep_events[i].data.fd, buf, BUF_SIZE);
                if (str_len == 0)
                {
                    epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL); //从epoll中删除套接字
                    close(ep_events[i].data.fd);
                    printf("closed client : %d \n", ep_events[i].data.fd);
                }
                else
                {
                    write(ep_events[i].data.fd, buf, str_len);
                }
            }
        }
    }
    close(serv_sock);
    close(epfd);

    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的Linux C epoll机制示例代码,用于监听文件描述符的可读事件: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/epoll.h> #define MAX_EVENTS 10 int main() { int fd, sockfd, n, epollfd; char buffer[256]; struct sockaddr_in serveraddr, clientaddr; socklen_t clientlen = sizeof(clientaddr); struct epoll_event ev, events[MAX_EVENTS]; // 创建socket sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("ERROR opening socket"); exit(1); } // 绑定地址 bzero((char *) &serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = INADDR_ANY; serveraddr.sin_port = htons(5000); if (bind(sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) { perror("ERROR on binding"); exit(1); } // 监听 listen(sockfd, 5); // 创建epoll实例 epollfd = epoll_create1(0); if (epollfd == -1) { perror("epoll_create1"); exit(EXIT_FAILURE); } // 添加sockfd到epoll实例中 ev.events = EPOLLIN; ev.data.fd = sockfd; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) { perror("epoll_ctl: sockfd"); exit(EXIT_FAILURE); } // 等待事件发生 while (1) { n = epoll_wait(epollfd, events, MAX_EVENTS, -1); for (int i = 0; i < n; i++) { // 如果是sockfd可读事件,则接收客户端连接 if (events[i].data.fd == sockfd) { fd = accept(sockfd, (struct sockaddr *) &clientaddr, &clientlen); if (fd < 0) { perror("ERROR on accept"); exit(1); } printf("Client connected\n"); // 将新的连接fd添加到epoll实例中 ev.events = EPOLLIN; ev.data.fd = fd; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { perror("epoll_ctl: fd"); exit(EXIT_FAILURE); } } // 如果是其他fd可读事件,则接收数据 else { bzero(buffer, 256); if (read(events[i].data.fd, buffer, 255) < 0) { perror("ERROR reading from socket"); exit(1); } printf("Message received: %s\n", buffer); } } } // 关闭socket和epoll实例 close(sockfd); close(epollfd); return 0; } ``` 以上代码监听端口5000,接收客户端连接并接收数据。通过epoll机制,可以同时监控多个文件描述符的可读事件,从而提高程序的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值