针对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];
for (int i=; 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);
int ret = recv(sockfd, buf, BUFFER_SIZE-, );
if (ret <= ) {
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];
for (int i=; 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);
int ret = recv(sockfd, buf, BUFFER_SIZE-, );
if (ret < ) {
// Below shows complete
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
printf("read later\n");
break;
}
printf("some error happens\n");
close(sockfd);
break;
}
else if (ret == ) {
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[]) {
if (argc <= ) {
printf("usage: %s port_number ip_address\n", basename(argv[]));
return ;
} int port = atoi(argv[]);
int ret = ;
sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
if (argc >= ) {
const char *ip =argv[];
inet_pton(AF_INET, ip, &address.sin_addr);
}
else {
address.sin_addr.s_addr = INADDR_ANY;
}
address.sin_port = htons(port); int listenfd = socket(PF_INET, SOCK_STREAM, );
assert(listenfd >= ); ret = bind(listenfd, (sockaddr*)&address, sizeof(address));
assert(ret != - ); ret = listen(listenfd, );
assert(ret != -); epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create();
assert(epollfd != -);
addfd(epollfd, listenfd, true); while(true) {
ret = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -);
if (ret < ) {
printf("epoll failure\n");
break;
}
lt(events, ret, epollfd, listenfd); // lt
//et(events, ret, epollfd, listenfd); // et
} close(listenfd);
return ; }

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 127.0.0.1
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
12345678901234567890123456
1234567890123456789012345678

在服务器端得到:

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

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

换成et模式:

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

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

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

服务器显示:

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

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

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

如下修改服务器代码:

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

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

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

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

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

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

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

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

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

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

                                else if (ret == ) {
printf("get 0 data\n");
close(sockfd);
break;
}

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

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

服务器端:

$./epoll_test
et event trigger once
get bytes of content:
get bytes of content:
get bytes of content:
get 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的两种工作模式

    epoll有两种模式,Edge Triggered(简称ET) 和 Level Triggered(简称LT).在採用这两种模式时要注意的是,假设採用ET模式,那么仅当状态发生变化时才会通知,而採用L ...

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

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

  4. epoll的LT和ET模式

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

  5. epoll的ET和LT模式

    epoll有两种模式,Edge Triggered(简称ET) 和 Level Triggered(简称LT). 在采用这两种模式时要注意的是,如果采用ET模式,那么仅当状态发生变化时才会通知,而采用 ...

  6. 我理解的epoll(三)多线程模式下的ET

    ET模式下,需要循环从缓存中读取,直到返回EAGAIN没有数据可读后,一个被通知的事件才算结束.如果还读取过程中,同一个连接又有新的事件到来,触发其他线程处理同一个socket,就乱了.EPOLL_O ...

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

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

  8. apache中配置php支持模块模式、cgi模式和fastcgi模式的实验

    首先安装apache.mysql和php,依次顺序安装. 1.apache.mysql的安装比较简单,略过 2. php的安装,我安装的是php5.3.6内置了php-fpm,所以不需要再单独下补丁了 ...

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

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

随机推荐

  1. c#读取Excel的列名问题

    在修改c#读取Excel的时候,遇到了一些小问题,总结下,希望别人不用再浪费时间 读取excel的时候,如果是空行就不读取? SELECT * FROM [DB_ESTATE$] where F2&l ...

  2. javaBean的使用方法;

    在jsp页面中使用javaBean:三个标签: <jsp:useBean>标签 <jsp:setProperty>标签 <jsp:getProperty>标签 首先 ...

  3. Spring资源访问

    资源访问 1.Resource Jdk提供的访问资源的类并不能很好地满足各种底层的资源访问需求, 比如缺少从类路径或者web容器的上下文中获取资源的操作类. 鉴于此, Spring设计了一个Resou ...

  4. 用WebDriver实现基于jira过滤器视图的统计自动化

    在Jira上通过过滤器我们可以做出多种视图,以方便统计我们想要收集的结果.比如:我想查看所有分派给我的任务.在Jira上,我保存了一个过滤器,叫做“分派给我的所有任务”.这个过滤器可以过滤出所有分配给 ...

  5. gulp 建立一个简单的自动化

    前端项目需要的功能: 1.图片(压缩图片支持jpg.png.gif) 2.样式 (支持sass 同时支持合并.压缩.重命名) 3.javascript (检查.合并.压缩.重命名) 4.html (压 ...

  6. Android LayoutInflater原理分析

    相信接触Android久一点的朋友对于LayoutInflater一定不会陌生,都会知道它主要是用于加载布局的.而刚接触Android的朋友可能对LayoutInflater不怎么熟悉,因为加载布局的 ...

  7. 1-NPM

    什么是NPM NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题. 常见的使用场景有以下几种: 允许用户从NPM服务器下载别人编写的第三方包到本地使用. 允许用户从N ...

  8. DataGridView的自定义列排序

    1,将需要进行排序的列做属性的设置 this.colUserName.SortMode = DataGridViewColumnSortMode.Programmatic; 2,添加列的事件 //点击 ...

  9. number_format

    number_format — 以千位分隔符方式格式化一个数字 说明 string number_format ( float $number [, int $decimals = 0 ] ) str ...

  10. UE4编程之C++创建一个FPS工程(一)创建模式&角色&处理输入

    转自:http://blog.csdn.net/u011707076/article/details/44180951 从今天开始,我们一起来学习一下,如何使用C++将一个不带有任何初学者内容的空模板 ...