epoll用法举例

RDP协议项目中需要获取键鼠事件回传给server,Linux下键鼠设备为 /dev/input/event*,直接读取即可。使用非阻塞IO肯定是不优雅的,一定要使用阻塞的IO,但是键鼠设备通常至少有3个,难道要开3个线程吗?epoll是Linux系统IO多路复用的接口,可以同时监视多个文件描述符状态,更详细的资料敲命令man 7 epoll

  • epoll 相关接口
//所需头文件
#include 

//主要结构体
typedef union epoll_data {
    void    *ptr;
    int      fd;
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;

struct epoll_event {
    uint32_t     events;
    epoll_data_t data;
};

//接口函数
int epoll_create (int size);
int epoll_ctl    (int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait   (int epfd, struct epoll_event *events, int maxevents, int timeout);
int close        (int fd);
  1. epoll_create ():在内核中创建一个 epoll 实例并返回一个文件描述符。参数size被忽略,但是要大于0。
  2. epoll_ctl:用于添加、删除、修改 被监视的文件描述符。op为下列值:
EPOLL_CTL_ADD     添加一个文件描述符到epoll实例。
EPOLL_CTL_MOD     改变一个文件描述符的被监视事件。
EPOLL_CTL_DEL     删除一个被监视的文件描述符。

event->events是要监视的事件,如下:

EPOLLIN         文件描述符可读。
EPOLLOUT        文件描述符可写
EPOLLRDHUP      对方断开连接
EPOLLPRI        带外数据
EPOLLERR        文件描述符发生错误
EPOLLHUP        文件描述符被挂断
EPOLLET         设为边缘触发模式
EPOLLONESHOT    只监视一次

event->data是用户私有数据,被监视的文件描述符有事件时,该值被原封不动地复制回来。

  1. epoll_wait():等待文件描述符事件,maxevents是buffer个数,timeout是超时时间,单位毫秒。
  2. close():关闭 epoll 实例。
  • epoll的几个性质:
    1.将epoll自己的文件描述符添加到自己中无效;
    2.epoll又可以监视别的epoll文件描述符;
    3.被监视的文件描述符被关闭后,也会自动从epoll上移除;
    4.epoll上被监视的文件描述符都被关闭后,内核会释放epoll实例。
    5.多个epoll实例可以监视相同的文件描述符,并都能收到文件描述符的事件;

  • 代码举例:

//==============================================================================
//  Copyright (C) 2019 王小康. All rights reserved.
//
//  作者: 王小康
//  描述: epoll用法举例
//  日期: 2019-04-30
//
//==============================================================================

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


static void function_key(short code, short flag){
    printf("\e[31m key  : %d %d \e[0m\n", code, flag);
}

static void function_move(short x, short y){
    printf("\e[32m move : %d %d \e[0m\n", x, y);
}

static void function_wheel(short flag){
    printf("\e[33m wheel: %d\e[0m\n", flag);
}

////////////////////////////////////////////////////////////////////////////////

static void readInputEvent(int fd){
    struct input_event inputEvent[32];
    int length = read(fd, inputEvent, sizeof(inputEvent));
    if(length <= 0) return;
    length /= sizeof(struct input_event);
    
    int i;
    short x = 0, y = 0;
    for(i=0; i 0 ? 1 : 0);
            }
        }
    }
    if(x || y){
        function_move(x, y);
    }
}

static int openInputEvent(int epollFd, char *name){
    char nameBuffer[32];
    snprintf(nameBuffer, 32, "/dev/input/%s", name);
    int fd = open(nameBuffer, O_RDWR);
    printf("open(%s) = %d\n", nameBuffer, fd);
    if(fd >= 0){
        struct epoll_event epollEvent;
        epollEvent.events = EPOLLIN | EPOLLERR;    //监视可读事件和错误事件
        epollEvent.data.fd = fd;                   //文件描述符放入私有数据中
        int err = epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &epollEvent);
        if(err < 0){
            close(fd);
            return 0;
        }
        else{
            return 1;
        }
    }
    else{
        return 0;
    }
}

int main(int argc, char* argv[]){
    
    //创建epoll实例
    int epollFd = epoll_create(8);
    if(epollFd < 0) return -1;
    
    //打开目录 /dev/input 下所有 event*,并添加到epoll。
    int n = 0;
    struct dirent *ent;
    DIR *dir = opendir("/dev/input");
    if(dir == NULL){
        close(epollFd);
        return -2;
    }
    while((ent = readdir(dir))){
        if(memcmp(ent->d_name, "event", 5) == 0){
            n += openInputEvent(epollFd, ent->d_name);
        }
    }
    closedir(dir);
    
    //检查添加了几个文件描述符到epoll
    if(n < 1){
        close(epollFd);
        return -3;
    }
    
    //等待可读的文件描述符,200次后退出。
    int err;
    struct epoll_event epollEvent;
    for(n=0; n<200; n++){
        epollEvent.events = 0;
        epollEvent.data.fd = 0;
        err = epoll_wait(epollFd, &epollEvent, 1, 1000);
        if(err < 0) break;
        if(err == 0) continue;
        if(epollEvent.events & EPOLLIN){
            readInputEvent(epollEvent.data.fd);
        }
        if(epollEvent.events & EPOLLERR){
            close(epollEvent.data.fd);
        }
    }
    
    close(epollFd);
    return 0;
}

  • 运行效果如图:

你可能感兴趣的:(epoll用法举例)