针对epoll api的两种触发模式,lt和et,仿照一些例子写了代码进行实验。

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <pthread.h>

#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#include <errno.h>
#include <string.h>
#include <fcntl.h>

#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 10

int setnonblocking(int fd) {
        int old_option = fcntl(fd, F_GETFL);
        int new_option = old_option | O_NONBLOCK;
        fcntl(fd, F_SETFL, new_option);
        return old_option;
}

void addfd(int epollfd, int fd, bool enable_et) {
        epoll_event event;
        event.data.fd = fd;
        event.events = EPOLLIN;
        if (enable_et) {
                event.events |= EPOLLET;
        }
        epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
        setnonblocking(fd);
}

void lt(epoll_event *events, int number, int epollfd, int listenfd) {
        char buf[BUFFER_SIZE];
        ; i<number; i++) {
                int sockfd = events[i].data.fd;
                if (sockfd == listenfd) {
                        sockaddr_in client_address;
                        socklen_t client_addrlen = sizeof(client_address);
                        int connfd = accept(listenfd, (sockaddr*)&client_address,
                                                &client_addrlen);
                        addfd(epollfd, connfd, false);
                }
                else if (events[i].events & EPOLLIN) {
                        printf("lt event trigger once\n");
                        memset(buf, '\0', BUFFER_SIZE);
                        , );
                        ) {
                                close(sockfd);
                                continue;
                        }
                        printf("get %d bytes of content: %s\n", ret, buf);
                }
                else {
                        printf("something else happened\n");
                }
        }
}

void et(epoll_event *events, int number, int epollfd, int listenfd) {
        char buf[BUFFER_SIZE];
        ; i<number; i++) {
                int sockfd = events[i].data.fd;
                if (sockfd == listenfd) {
                        sockaddr_in client_address;
                        socklen_t client_addrlen = sizeof(client_address);
                        int connfd = accept(listenfd, (sockaddr*)&client_address,
                                                &client_addrlen);
                        addfd(epollfd, connfd, true);
                }
                else if(events[i].events & EPOLLIN) {
                        // Need to read complete
                        printf("et event trigger once\n");
                        while (true) {
                                memset(buf, '\0', BUFFER_SIZE);
                                , );
                                ) {
                                        // Below shows complete
                                        if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                                                printf("read later\n");
                                                break;
                                        }
                                        printf("some error happens\n");
                                        close(sockfd);
                                        break;
                                }
                                ) {
                                        close(sockfd);
                                        break;
                                }
                                else {
                                        printf("get %d bytes of content: %s\n", ret, buf);
                                }
                        }
                }
                else {
                        printf("something else happened\n");
                }
        }
}

int main(int argc, char *argv[]) {
        ) {
                printf(]));
                ;
        }

        ]);
        ;
        sockaddr_in address;
        bzero(&address, sizeof(address));
        address.sin_family = AF_INET;
        ) {
                ];
                inet_pton(AF_INET, ip, &address.sin_addr);
        }
        else {
                address.sin_addr.s_addr = INADDR_ANY;
        }
        address.sin_port = htons(port);

        );
        assert(listenfd >= );

        ret = bind(listenfd, (sockaddr*)&address, sizeof(address));
        assert(ret != - );

        ret = listen(listenfd, );
        assert(ret != -);

        epoll_event events[MAX_EVENT_NUMBER];
        );
        assert(epollfd != -);
        addfd(epollfd, listenfd, true);

        while(true) {
                ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -);
                ) {
                        printf("epoll failure\n");
                        break;
                }
                lt(events, ret, epollfd, listenfd); // lt
                //et(events, ret, epollfd, listenfd); // et
        }

        close(listenfd);
        ;

}

Makefile文件:

epoll_test : epoll_test.cpp
        g++ -o epoll_test epoll_test.cpp -lpthread

以上程序有个问题,就是在端口被占用时候,因为bind失败,会assert失败然后core dump. 在重复测试时候,可以换个端口。

首先,注释掉et,使用lt:

                lt(events, ret, epollfd, listenfd); // lt
                //et(events, ret, epollfd, listenfd); // et

运行 ./epoll_test 12888 并在另一个窗口用telnet输入超过10个(BUFFERSIZE)字符:

$telnet
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
123456789012345678901234561234567890123456789012345678

在服务器端得到:

$./epoll_test
lt event trigger once
 bytes of content:
lt event trigger once
 bytes of content:
lt event trigger once
 bytes of content:
lt event trigger once
 bytes of content: 

lt event trigger once
 bytes of content:
lt event trigger once
 bytes of content:
lt event trigger once
 bytes of content:
lt event trigger once
 bytes of content: 

可以看出因为buffersize的限制,服务器端进行了多次读取,event也触发了多次。

换成et模式:

                //lt(events, ret, epollfd, listenfd); // lt
                et(events, ret, epollfd, listenfd); // et

运行服务器后,telnet客户端输入:

$telnet
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

服务器显示:

$./epoll_test
et event trigger once
 bytes of content:
 bytes of content:
 bytes of content:
 bytes of content: 

read later
et event trigger once
 bytes of content:
 bytes of content:
 bytes of content:
 bytes of content: 

read later

可以看出,每次客户端的字符串,只触发了一次。

其实,上面的例子还不够严谨,因为服务器一次已经把字符都读完了。那么如果没读完,会继续出发吗。

如下修改服务器代码:

                        //修改
                        //while (true) {
                        if (true) {
                                memset(buf, '\0', BUFFER_SIZE);
                                , );
                                ) {
                                        // Below shows complete
                                        if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                                                printf("read later\n");
                                                break;
                                        }
                                        printf("some error happens\n");
                                        close(sockfd);
                                        //修改
                                        //break;
                                }
                                ) {
                                        close(sockfd);
                                        //修改
                                        //break;
                                }
                                else {
                                        printf("get %d bytes of content: %s\n", ret, buf);
                                }
                        }        

运行服务器之后,telnet输入长字符串:

$telnet
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

服务器端只显示了BUFFERSIZE长度的一行,没有读入的数据也没有进行event触发:

$./epoll_test
et event trigger once
 bytes of content: 

如果客户端,再输入一行:

$telnet
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

服务器端也仅仅把之前没读入的上一次客户端发来的数据中,再读入BUFFERSIZE长度:

$./epoll_test
et event trigger once
 bytes of content:
et event trigger once
 bytes of content: 

另外,对上面的服务器端程序,增加了et模式下对recv函数的返回ret=0的打印:

                                ) {
                                        printf("get 0 data\n");
                                        close(sockfd);
                                        break;
                                }

发现在et模式下,没有走到ret==0的分支:

$telnet
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

服务器端:

$./epoll_test
et event trigger once
 bytes of content:
 bytes of content:
 bytes of content:
 bytes of content: 

read later

走的是如下的判断结束的分支:

                                        if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
                                                printf("read later\n");
                                                break;
                                        }

以上。:)

epoll的lt和et模式的实验的更多相关文章

  1. Epoll之ET、LT模式

    Epoll之ET.LT模式 在使用epoll时,在函数 epoll_ctl中如果不设定,epoll_event 的event默认为LT(水平触发)模式. 使用LT模式意味着只要fd处于可读或者可写状态 ...

  2. Epoll在LT和ET模式下的读写方式

    在一个非阻塞的socket上调用read/write函数, 返回EAGAIN或者EWOULDBLOCK(注: EAGAIN就是EWOULDBLOCK) 从字面上看, 意思是:EAGAIN: 再试一次, ...

  3. epoll的LT和ET模式

    原理參考该博客 从man手冊中,得到ET和LT的详细描写叙述例如以下 EPOLL事件有两种模型: Edge Triggered (ET) Level Triggered (LT) 假如有这样一个样例: ...

  4. epoll的ET和LT模式比较 - 源码分析

    eventpoll是一种文件,它实现了一种机制利用一条rdllist队列来避免阻塞地进行poll.eventpoll归根到底还是在使用poll.而ET比LT高效,并不在于是否使用了poll,更不能说是 ...

  5. epoll的内部实现 &amp; 百万级别句柄监听 &amp; lt和et模式非常好的解释

    epoll是Linux高效网络的基础,比如event poll(例如nodejs),是使用libev,而libev的底层就是epoll(只不过不同的平台可能用epoll,可能用kqueue). epo ...

  6. linux下epoll实现机制

    linux下epoll实现机制 原作者:陶辉 链接:http://blog.csdn.net/russell_tao/article/details/7160071 先简单回顾下如何使用C库封装的se ...

  7. 驳 GarbageMan 的《一个超复杂的简介递归》——对延迟计算的实验和思考

    这是一篇因骂战而起的博文,GarbageMan 在该文章回复中不仅对我进行了侮辱,还涉及了我的母校,特写此文用理性的分析和实验予以回击. 在此也劝告 GarbageMan,没什么本事就别在那叫嚣了,还 ...

  8. linux下epoll如何实现高效处理百万句柄的

    linux下epoll如何实现高效处理百万句柄的 分类: linux 技术分享 2012-01-06 10:29 4447人阅读 评论(5) 收藏 举报 linuxsocketcachestructl ...

  9. 高并发网络编程之epoll详解

    select.poll和epoll的区别 在linux没有实现epoll事件驱动机制之前,我们一般选择用select或者poll等IO多路复用的方法来实现并发服务程序.在大数据.高并发.集群等一些名词 ...

随机推荐

  1. VS调试程序时一闪而过的问题-解决方法(网上搜集)

    在VS2012里的控制台应用程序在运行时,结果画面一闪而过,不管是用F5 还是用Ctrl + F5都是一样,导致无法看到结果. 网上有不少的办法,说是都是在程序最后加一个要程序暂停的语句或从控制台上获 ...

  2. springMVC学习笔记(一)-----springMVC原理

    一.什么是springmvc springMVC是spring框架的一个模块,springMVC和spring无需通过中间整合层进行开发. springMVC是一个基于mvc的web框架. Sprin ...

  3. (一)安卓小app开发之基础环境搭建

    一.准备工作: 1.下载Android Studio开发环境 https://dl.google.com/dl/android/studio/ide-zips/2.1.1.0/android-stud ...

  4. KVM虚拟化技术(七)虚拟机配置文件

    KVM虚拟机的配置文件在/etc/libvirt/qemu/下,为xml文件 整体结构如下: <domain type='kvm'> 虚拟机整体信息 系统信息 硬件资源特性 突发事件处理 ...

  5. 5.2视图中的Order by

    创建排序视图的企图本身就是错误的,因为视图表示一个表,而表是不会对行排序的:

  6. 机器学习中的范数规则化之(一)L0、L1与L2范数

    L1正则会产生稀疏解,让很多无用的特征的系数变为0,只留下一些有用的特征 L2正则不让某些特征的系数变为0,即不产生稀疏解,只让他们接近于0.即L2正则倾向于让权重w变小.见第二篇的推导. 所以,样本 ...

  7. CSS3 transform 属性详解(skew, rotate, translate, scale)

    写这篇文章是因为在一个前端QQ群里,网友 "小豆豆" (应他要求要出现他的网名......) ,问skew的角度怎么算,因为他看了很多文章还是不能理解skew的原理.于是,我觉得有 ...

  8. 我的 GitHub 100 连击

    终于达成 gayhub 的第一个100连击了,感觉自己整个人颜色都不一样了,完全蜕变了. PS: GitHub 汉化插件 52cik/github-hans 感兴趣的赶紧 get 起来吧. 遇到瓶颈 ...

  9. 结合Hadoop,简单理解SSH

    在启动dfs和yarn时,需要多次输入密码,不但启动本机进程还有辅服务器启动那些节点也需要相应密码,主与辅服务器之间是通过SSH连接的,并发送操作指令 一.ssh密码远程登录 1.使用ssh连接另一台 ...

  10. C#实现MD5加密

    摘自:http://blog.csdn.net/shenghui188/archive/2010/03/28/5423959.aspx 方法一 首先,先简单介绍一下MD5 MD5的全称是message ...