自從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 }