转载的,感觉不错,所以留下备份
来自于:http://m.cnblogs.com/62707/1681172.html
epoll是Kernel 2.6后新加入的事件机制,在高并发条件下,远优于select.
用个硬件中的例子吧,可能不太恰当:epoll相当于I/O中断(有的时候才相应),而select相当于轮询(总要反复查询)。
其实epoll比slect好用很多, 主要一下几个用法。
struct epoll_event ;epoll事件体,事件发生时候你可以得到一个它。其中epoll_event.data.fd可以存储关联的句柄,epoll_event.event是监听标志,常用的有EPOLLIN (有数据,可以读)、EPOLLOUT(有数据,可以写)EPOLLET(有事件,通用);
(1) 创建epoll句柄
int epFd = epoll_create(EPOLL_SIZE);
(2)加入一个句柄到 epoll的监听队列
ev.data.fd = serverFd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epFd, EPOLL_CTL_ADD, serverFd, &ev);
上 面的fd是你要绑定给事件发生时候使用的fd,到时候只能操作这个,下面是事件类型。
使用epoll_ctl添加到之中,EPOLL_CTL_ADD是epoll控制类型,这里监听的fd和给event的fd一般相同。
(3)等待event返回
int nfds = epoll_wait(epFd, evs, EVENT_ARR, -1);
传入的evs是epoll_event的数组,EVENT_ARR应当是不超过这个数组的长度。返回nfds的是不超过EVENT_ARR的数值,表示本次等待到了几个事件。
(4)遍历事件
注意,这里遍历的事件是肯定已经发生了的,而select中遍历的是每个fd,而fd不一定在FDSET中(即不一定有读事件发生)!这是效率最大的差别所在!
for (int i = 0; i < nfds; i++)
{
//do something
}
(5)其他技巧
对事件是否是 serverFd判断,如果是,进行accept并加入epoll监听队列,要设置异步读取。
如果evts[i]&EPOLLIN,表示可读,使用read进行试探,如果>0表示连接没有关闭,否则连接已经关闭(出发事件又读取不到东西,表示socket关闭!)。如果<0出错。如果>0,需要继续读取直到为0,但是注意这里的为0是在第一次read不为0的前提下,毕竟我们设置了异步读取,暂时没有数据可以读就返回0了!而如果第一次返回0,那么就是关闭吧!
注意关闭要移出出epoll并且 close(clientFd)
罗嗦了好多,看代码!
1
|
11
|
#include<stdio.h>
|
12
|
#include<stdlib.h>
|
13
|
#include<unistd.h>
|
14
|
#include<fcntl.h>
|
15
|
#include<arpa/inet.h>
|
16
|
#include<netinet/in.h>
|
17
|
#include<sys/epoll.h>
|
18
|
#include<errno.h>
|
19
|
20
|
#define EPOLL_SIZE 10
|
21
|
#define EVENT_ARR 20
|
22
|
#define BACK_QUEUE 10
|
23
|
#define PORT 18001
|
24
|
#define BUF_SIZE 16
|
25
|
26
|
void setnonblocking( int sockFd) {
|
27
|
int opt;
|
28
|
29
|
//获取sock原来的flag
|
30
|
opt= fcntl(sockFd, F_GETFL);
|
31
|
if (opt < 0){
|
32
|
printf ( "fcntl(F_GETFL) fail." );
|
33
|
exit (-1);
|
34
|
}
|
35
|
36
|
//设置新的flag,非阻塞
|
37
|
opt|= O_NONBLOCK;
|
38
|
if (fcntl(sockFd, F_SETFL, opt)< 0) {
|
39
|
printf ( "fcntl(F_SETFL) fail." );
|
40
|
exit (-1);
|
41
|
}
|
42
|
}
|
43
|
44
|
int main(){
|
45
|
46
|
int serverFd;
|
47
|
48
|
//创建服务器fd
|
49
|
serverFd= socket(AF_INET, SOCK_STREAM, 0);
|
50
|
setnonblocking(serverFd);
|
51
|
52
|
//创建epoll,并把serverFd放入监听队列
|
53
|
int epFd =epoll_create(EPOLL_SIZE);
|
54
|
struct epoll_event ev,evs[EVENT_ARR];
|
55
|
ev.data.fd= serverFd;
|
56
|
ev.events= EPOLLIN | EPOLLET;
|
57
|
epoll_ctl(epFd,EPOLL_CTL_ADD, serverFd, &ev);
|
58
|
59
|
//绑定服务器端口
|
60
|
struct sockaddr_inserverAddr;
|
61
|
socklen_tserverLen = sizeof ( struct sockaddr_in);
|
62
|
serverAddr.sin_addr.s_addr= htonl(INADDR_ANY);
|
63
|
serverAddr.sin_port= htons(PORT);
|
64
|
if (bind(serverFd,( struct sockaddr *) &serverAddr, serverLen)){
|
65
|
printf ( "bind()fail.\n" );
|
66
|
exit (-1);
|
67
|
}
|
68
|
69
|
//打开监听
|
70
|
if (listen(serverFd, BACK_QUEUE)){
|
71
|
printf ( "Listenfail.\n" );
|
72
|
exit (-1);
|
73
|
}
|
74
|
75
|
//死循环处理
|
76
|
int clientFd;
|
77
|
sockaddr_inclientAddr;
|
78
|
socklen_tclientLen;
|
79
|
char buf[BUF_SIZE];
|
80
|
while (1) {
|
81
|
//等待epoll事件的到来,最多取EVENT_ARR个事件
|
82
|
int nfds = epoll_wait(epFd, evs,EVENT_ARR, -1);
|
83
|
//处理事件
|
84
|
for ( int i = 0; i < nfds; i++){
|
85
|
if (evs[i].data.fd == serverFd&& evs[i].data.fd &EPOLLIN) {
|
86
|
//如果是serverFd,表明有新连接连入
|
87
|
if ((clientFd =accept(serverFd,
|
88
|
( struct sockaddr *)&clientAddr, &clientLen))< 0) {
|
89
|
printf ( "acceptfail.\n" );
|
90
|
}
|
91
|
printf ( "Connect from %s:%d\n" ,inet_ntoa(clientAddr.sin_addr),
|
92
|
htons(clientAddr.sin_port));
|
93
|
<br />
setnonblocking(clientFd);
|
94
|
//注册accept()到的连接
|
95
|
ev.data.fd= clientFd;
|
96
|
ev.events= EPOLLIN | EPOLLET;
|
97
|
epoll_ctl(epFd,EPOLL_CTL_ADD, clientFd, &ev);
|
98
|
} else if (evs[i].events &EPOLLIN) {
|
99
|
//如果不是serverFd,则是client的可读
|
100
|
if ((clientFd = evs[i].data.fd)> 0) {
|
101
|
//先进行试探性读取
|
102
|
int len = read(clientFd, buf,BUF_SIZE);
|
103
|
if (len > 0){
|
104
|
//有数据可以读,Echo写入
|
105
|
do {
|
106
|
if (write(clientFd, buf, len)< 0) {
|
107
|
printf ( "write() fail.\n" );
|
108
|
}
|
109
|
len= read(clientFd, buf, BUF_SIZE);
|
110
|
} while (len> 0);
|
111
|
} else if (len == 0) {
|
112
|
//出发了EPOLLIN事件,却没有可以读取的,表示断线
|
113
|
printf ( "Clientclosed at %d\n" , clientFd);
|
114
|
epoll_ctl(epFd,EPOLL_CTL_DEL, clientFd, &ev);
|
115
|
close(clientFd);
|
116
|
evs[i].data.fd= -1;
|
117
|
break ;
|
118
|
} else if (len == EAGAIN) {
|
119
|
continue ;
|
120
|
} else {
|
121
|
//client读取出错
|
122
|
printf ( "read()fail." );
|
123
|
}
|
124
|
}
|
125
|
} else {
|
126
|
printf ( "otherevent.\n" );
|
127
|
}
|
128
|
}
|
129
|
}
|
130
|
131
|
return 0;
|
132
|
}
|