Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!!
Edge_triggered(边缘触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!
下面的代码有注解
#include
#include
#include
#include
#include
#include //for bzero
#include
#include
#include
using namespace std;
#define MAX_LINE 20
#define MAX_EVENTS 500
#define MAX_LISTENFD 5
int createAndListen()
{
int on = 1;
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in servaddr;
fcntl(listenfd, F_SETFL, O_NONBLOCK); //no-block io
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(11112);
if(-1 == bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)))
{
cout << "bind error, errno:" << errno << endl;
}
if(-1 == listen(listenfd, MAX_LISTENFD))
{
cout << "listen error, errno:" << errno << endl;
}
return listenfd;
}
int main(int args, char** argv)
{
struct epoll_event ev, events[MAX_EVENTS];
int listenfd,connfd,sockfd;
int readlength;
char line[MAX_LINE];
struct sockaddr_in cliaddr;
socklen_t clilen = sizeof(struct sockaddr_in);
int epollfd = epoll_create(1);
if (epollfd < 0)
cout << "epoll_create error, error:" << epollfd << endl;
listenfd = createAndListen();
ev.data.fd = listenfd;
ev.events = EPOLLIN;
epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev);
for(;;)
{
int fds = epoll_wait(epollfd, events, MAX_EVENTS, -1); //在EPOLLET模式下,read(sockfd, line, N),如果N > 缓冲期数据真实大小,这里只会触发一次,EPOLLLT模式下不管IO是否阻塞都会多次触发
if(fds == -1)
{
cout << "epoll_wait error, errno:" << errno << endl;
break;
}
else {
printf("trig %d !!!\n", fds);
}
for(int i = 0; i < fds; i++)
{
if(events[i].data.fd == listenfd)
{
connfd = accept(listenfd, (sockaddr*)&cliaddr, (socklen_t*)&clilen);
if(connfd > 0)
{
cout << "new connection from "
<< "[" << inet_ntoa(cliaddr.sin_addr)
<< ":" << ntohs(cliaddr.sin_port) << "]"
<< " accept socket fd:" << connfd
<< endl;
}
else
{
cout << "accept error, connfd:" << connfd
<< " errno:" << errno << endl;
}
fcntl(connfd, F_SETFL, O_NONBLOCK); //no-block io
ev.data.fd = connfd;
ev.events = EPOLLIN | EPOLLET;
if( -1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev))
cout << "epoll_ctrl error, errno:" << errno << endl;
}
else if(events[i].events & EPOLLIN)
{
if((sockfd = events[i].data.fd) < 0)
{
cout << "EPOLLIN sockfd < 0 error " << endl;
continue;
}
bzero(line, MAX_LINE);
if((readlength = read(sockfd, line, MAX_LINE)) < 0)
{
if(errno == ECONNRESET)
{
cout << "ECONNREST closed socket fd:" << events[i].data.fd << endl;
close(sockfd);
}
}
else if( readlength == 0)
{
cout << "read 0 closed socket fd:" << events[i].data.fd << endl;
close(sockfd);
}
else
{
printf("read %d, content is %s !!!\n", readlength, line);
//if(write(sockfd, line, readlength) != readlength)
//cout << "error: not finished one time" << endl;
}
}
}
}
return 0;
}