epoll用法举例

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

  • epoll 相关接口
//所需头文件
#include <sys/epoll.h>

//主要结构体
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 <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <dirent.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/epoll.h>


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<length; i++){
        if(inputEvent[i].type == EV_KEY){
            function_key(inputEvent[i].code, inputEvent[i].value ? 1 : 0);
        }
        else if(inputEvent[i].type == EV_REL){
            if(inputEvent[i].code == REL_X){
                x += inputEvent[i].value;
            }
            else if(inputEvent[i].code == REL_Y){
                y += inputEvent[i].value;
            }
            else if(inputEvent[i].code == REL_WHEEL){
                function_wheel(inputEvent[i].value > 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;
}

  • 运行效果如图:
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • 前言 这篇文章读不懂的没关系,可以先收藏一下。笔者准备介绍完epoll和NIO等知识点,然后写一篇Java网络IO...
    星夜兼程工作笔记阅读 449评论 0 0
  • 一、Unix/Linux网络IO模型在经典的Unix网络编程中,总结出了5种不同的网络IO模型,分别是阻塞式IO,...
    布衣小菜阅读 6,017评论 0 3
  • 0、引用 http://www.cnblogs.com/Anker/p/3265058.html http://j...
    jolanxiao阅读 1,217评论 0 2
  • epoll概述 epoll是linux中IO多路复用的一种机制,I/O多路复用就是通过一种机制,一个进程可以监视多...
    发仔很忙阅读 10,859评论 4 35
  • 推荐指数: 6.0 书籍主旨关键词:特权、焦点、注意力、语言联想、情景联想 观点: 1.统计学现在叫数据分析,社会...
    Jenaral阅读 5,700评论 0 5