自从Linux提供了/dev/epoll的设备以及后来2.6内核中对/dev/epoll设备的访问的封装(System Epoll)之后,这种现象得到了大大的缓解,如果说几个月前,大家还对epoll不熟悉,那么现在来说的话,epoll的应用已经得到了大范围的普及。
那么究竟如何来使用epoll呢?其实非常简单。
通过在包含一个头文件#include <sys/epoll.h>以及几个简单的API将可以大大的提高你的网络服务器的支持人数。
首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你epoll所支持的最大句柄数。这个函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。
之后在你的网络主循环里面,每一帧的调用epoll_wait(int epfd, epoll_event events, int max events, int timeout)来查询所有的网络接口,看哪一个可以读,哪一个可以写了。基本的语法为:
nfds = epoll_wait(kdpfd, events, maxevents, -1);
其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout 是epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件范围,为任意正整数的时候表示等这么长的时间,如果一直没有事件,则范围。一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率。
epoll_wait范围之后应该是一个循环,遍利所有的事件:
for(n = 0; n < nfds; ++n) {
if(events[n].data.fd == listener) { //如果是主socket的事件的话,则表示有新连接进入了,进行新连接的处理。
client = accept(listener, (struct sockaddr *) &local, &addrlen);
if(client < 0){
perror("accept");
continue;
}
setnonblocking(client); // 将新连接置于非阻塞模式
ev.events = EPOLLIN | EPOLLET; // 并且将新连接也加入EPOLL的监听队列。
注意,这里的参数EPOLLIN | EPOLLET并没有设置对写socket的监听,如果有写操作的话,这个时候epoll是不会返回事件的,如果要对写操作也监听的话,应该是EPOLLIN | EPOLLOUT | EPOLLET
ev.data.fd = client;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
// 设置好event之后,将这个新的event通过epoll_ctl加入到epoll的监听队列里面,这里用EPOLL_CTL_ADD来加一个新的 epoll事件,通过EPOLL_CTL_DEL来减少一个epoll事件,通过EPOLL_CTL_MOD来改变一个事件的监听方式。
fprintf(stderr, "epoll set insertion error: fd=%d0, client);
return -1;
}
}
else // 如果不是主socket的事件的话,则代表是一个用户socket的事件,则来处理这个用户socket的事情,比如说read(fd,xxx)之类的,或者一些其他的处理。
do_use_fd(events[n].data.fd);
}
对,epoll的操作就这么简单,总共不过4个API:epoll_create, epoll_ctl, epoll_wait和close。
1 #include <stdlib.h>
2 #include <sys/socket.h>
3 #include <sys/epoll.h>
4 #include <netinet/in.h>
5 #include <arpa/inet.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 #include <stdio.h>
9 #include <errno.h>
10 #include<string.h>
11
12 #define MAXLINE 1024
13 #define LISTEN 10
14 #define PORT 5000
15
16 void setnonblocking(int sock)
17 {
18 int ret;
19 ret = fcntl(sock,F_GETFL);
20 if(ret<0){
21 perror("fcntl get");
22 exit(1);
23 }
24
25 ret=ret|O_NONBLOCK;
26
27 if(fcntl(sock,F_SETFL,ret)<0){
28 perror("fcntl set");
29 exit(1);
30 }
31 }
32
33 int main()
34 {
35 int sock,conn,epfd,nfds,max;
36 struct sockaddr_in serveraddr,clientaddr;
37 int n,yes=1,i,fd;
38 char line[MAXLINE];
39 socklen_t len;
40 char *localaddr="127.0.0.1";
41
42 struct epoll_event ev,events[20];
43
44 epfd = epoll_create(256);
45
46 memset(&serveraddr,0,sizeof(struct sockaddr_in));
47 memset(&clientaddr,0,sizeof(struct sockaddr_in));
48
49 sock = socket(AF_INET,SOCK_STREAM,0);
50 if(sock<0){
51 perror("socket");
52 exit(1);
53 }
54
55 if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&yes,((socklen_t)sizeof(yes)))<0){
56 perror("setsockopt");
57 exit(1);
58 }
59
60 setnonblocking(sock);
61
62
63 ev.data.fd=sock;
64 ev.events = EPOLLIN|EPOLLET;
65
66 epoll_ctl(epfd,EPOLL_CTL_ADD,sock,&ev);
67
68 serveraddr.sin_family = AF_INET;
69 serveraddr.sin_port = htons(PORT);
70 inet_aton(localaddr,&(serveraddr.sin_addr));
71
72 if(bind(sock,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){
73 perror("bind");
74 exit(1);
75 }
76
77 if(listen(sock,LISTEN)<0){
78 perror("listen");
79 exit(1);
80 }
81
82 for(;;)
83 {
84 nfds = epoll_wait(epfd,events,20,500);
85 for(i=0;i<nfds;i++)
86 {
87 if(events[i].data.fd == sock){
88 len = sizeof(clientaddr);
89 conn = accept(sock,(struct sockaddr*)&clientaddr,&len);
90 if(conn<0){
91 perror("accept");
92 exit(1);
93 }
94
95 setnonblocking(conn);
96
97 char *str = inet_ntoa(clientaddr.sin_addr);
98 printf("Accept a connection from %s\n",str);
99
100 ev.data.fd = conn;
101 ev.events = EPOLLIN|EPOLLET;
102
103 epoll_ctl(epfd,EPOLL_CTL_ADD,conn,&ev);
104 }
105 else if(events[i].events&EPOLLIN){
106 printf("=============EPOLLIN=============\n");
107 if((fd = events[i].data.fd)<0){
108 continue;
109 }
110
111 memset(line,0,MAXLINE);
112 n=read(fd,line,MAXLINE);
113 if(n<0){
114 if(errno==ECONNRESET||errno==EINTR){
115 close(fd);
116 events[i].data.fd=-1;
117 }
118 else{
119 perror("Read");
120 exit(1);
121 }
122 }
123 else if(n==0){
124 close(fd);
125 events[i].data.fd=-1;
126 }
127
128 line[n+1]='\0';
129
130 printf("Read from client:%s\n",line);
131
132 ev.data.fd=fd;
133 ev.events = EPOLLOUT|EPOLLET;
134 //修改sockfd上要处理的事件为EPOLLOUT
135 epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
136 }
137 else if(events[i].events&EPOLLOUT){
138 printf("===============EPOLLOUT==================\n");
139 int ret;
140
141 fd=events[i].data.fd;
142 if((fd = events[i].data.fd)<0){
143 continue;
144 }
145
146 ret=write(fd,line,n+1);
147 if(ret<0){
148 if(errno==ECONNRESET||errno==EINTR){
149 close(fd);
150 events[i].data.fd=-1;
151 }
152 else{
153 perror("Write");
154 exit(1);
155 }
156 }
157 else if(ret==0){
158 close(fd);
159 events[i].data.fd=-1;
160 }
161
162 printf("Write to client:%s\n",line);
163 memset(line,0,MAXLINE);
164
165 ev.events =EPOLLIN|EPOLLET;
166 //修改sockfd上要处理的事件为EPOLLIT
167 epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
168 }
169 }
170 }
171 return 0;
172 }