頭文件:#include <sys/epoll.h>
一.eopll相關的函數:
1.int epoll_create(int size);
返回一個epoll句柄,參數size是可監聽的最大個數
2.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
參數一epfd是epoll_create()出來到句柄。
參數二op可以取三個宏中到其一:
EPOLL_CTL_ADD:注冊新的fd到epfd中;
EPOLL_CTL_MOD:修改已經注冊的fd的監聽事件;
EPOLL_CTL_DEL:從epfd中刪除一個fd;
參數三fd是要監聽的fd。
參數四event是設置監聽到模式(ET模式,LT模式,write事件,read事件,accept事件)
相關結構體如下:
typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events可以是以下幾個宏的集合:
EPOLLIN :表示對應的文件描述符可以讀(包括對端SOCKET正常關閉);
EPOLLOUT:表示對應的文件描述符可以寫;
EPOLLPRI:表示對應的文件描述符有緊急的數據可讀(這裡應該表示有帶外數據到來);
EPOLLERR:表示對應的文件描述符發生錯誤;
EPOLLHUP:表示對應的文件描述符被掛斷;
EPOLLET: 將EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對於水平觸發(Level Triggered)來說的。
EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL隊列裡。
epoll_event.data.fd是要監聽到fd,一般設置epoll_event.data.fd和epoll_event.events就足夠了。
3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
返回值是觸發事件到個數,如返回0表示已超時。
參數一epfd是epoll_create()出來到句柄。
參數二events是一個epoll_event類型的數組。
參數三maxevents的值不能大於創建epoll_create()時的size。
參數四timeout是超時時間(毫秒,0會立即返回,-1將不確定,永久阻塞)。
4.close(int fd)
關閉句柄。
二.代碼
1.服務器
1 #include <sys/socket.h>
2 #include <sys/epoll.h>
3 #include <netinet/in.h>
4 #include <arpa/inet.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <stdio.h>
8 #include <errno.h>
9 #include <string.h>
10
11
12
13 #define bool int
14 #define false 0
15 #define true 1
16
17 #define MAXLINE 5
18 #define OPEN_MAX 100
19 #define LISTENQ 20
20 #define SERV_PORT 5000
21 #define INFTIM 1000
22
23 void setnonblocking(int sock)
24 {
25 int opts;
26 opts=fcntl(sock,F_GETFL);
27 if(opts<0)
28 {
29 perror("fcntl(sock,GETFL)");
30 return;
31 }
32 opts = opts|O_NONBLOCK;
33 if(fcntl(sock,F_SETFL,opts)<0)
34 {
35 perror("fcntl(sock,SETFL,opts)");
36 return;
37 }
38 }
39
40 void CloseAndDisable(int sockid, struct epoll_event ee)
41 {
42 close(sockid);
43 ee.data.fd = -1;
44 }
45
46 int main()
47 {
48 int i, maxi, listenfd, connfd, sockfd,epfd,nfds, portnumber;
49 char line[MAXLINE];
50 socklen_t clilen;
51
52 struct sockaddr_in clientaddr;
53 struct sockaddr_in serveraddr;
54
55
56 portnumber = 5000;
57
58 //聲明epoll_event結構體的變量,ev用於注冊事件,數組用於回傳要處理的事件
59
60 struct epoll_event ev,events[20];
61 //生成用於處理accept的epoll專用的文件描述符
62
63 epfd=epoll_create(256);
64
65 listenfd = socket(AF_INET, SOCK_STREAM, 0);
66
67 memset(&serveraddr, 0, sizeof(serveraddr));
68 serveraddr.sin_family = AF_INET;
69 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
70 serveraddr.sin_port=htons(portnumber);
71
72 // bind and listen
73 //no-blocking
74 setnonblocking(listenfd);
75 bind(listenfd,(struct sockaddr *)&serveraddr, sizeof(serveraddr));
76 if(0 != listen(listenfd, LISTENQ)){
77 perror("listen !=0");
78 }
79
80
81 //設置與要處理的事件相關的文件描述符
82 ev.data.fd=listenfd;
83 //設置要處理的事件類型
84 ev.events=EPOLLIN|EPOLLET;
85 //ev.events=EPOLLIN;
86
87 //注冊epoll事件
88 epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
89
90 maxi = 0;
91
92 int bOut = 0;
93 for ( ; ; )
94 {
95 if (bOut == 1)
96 break;
97 //等待epoll事件的發生
98
99 nfds=epoll_wait(epfd,events,20,-1);
100 //處理所發生的所有事件
101
102 printf("\nepoll_wait returns\n");
103
104 for(i=0;i<nfds;++i)
105 {
106 if(events[i].data.fd==listenfd)//如果新監測到一個SOCKET用戶連接到了綁定的SOCKET端口,建立新的連接。
107 {
108 connfd = accept(listenfd,(struct sockaddr *)&clientaddr, &clilen);
109 if(connfd<0){
110 perror("connfd<0");
111 return (1);
112 }
113
114
115 char *str = inet_ntoa(clientaddr.sin_addr);
116
117 printf("accapt a connection from %s\n",str);
118 //設置用於讀操作的文件描述符
119
120 setnonblocking(connfd);
121 ev.data.fd=connfd;
122 //設置用於注測的讀操作事件
123
124 ev.events=EPOLLIN | EPOLLET;
125 //ev.events=EPOLLIN;
126
127 //注冊ev
128 epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
129 }
130 else if(events[i].events & EPOLLIN)//如果是已經連接的用戶,並且收到數據,那麼進行讀入。
131 {
132
133 printf("EPOLLIN\n");
134 if ( (sockfd = events[i].data.fd) < 0)
135 continue;
136
137 char * head = line;
138 int recvNum = 0;
139 int count = 0;
140 bool bReadOk = false;
141 while(1)
142 {
143 // 確保sockfd是nonblocking的
144 recvNum = recv(sockfd, head + count, MAXLINE, 0);
145 if(recvNum < 0)
146 {
147 if(errno == EAGAIN)
148 {
149 // 由於是非阻塞的模式,所以當errno為EAGAIN時,表示當前緩沖區已無數據可讀
150 // 在這裡就當作是該次事件已處理處.
151 bReadOk = true;
152 break;
153 }
154 else if (errno == ECONNRESET)
155 {
156 // 對方發送了RST
157 CloseAndDisable(sockfd, events[i]);
158
159 printf("counterpart send out RST\n");
160 break;
161 }
162 else if (errno == EINTR)
163 {
164 // 被信號中斷
165 continue;
166 }
167 else
168 {
169 //其他不可彌補的錯誤
170 CloseAndDisable(sockfd, events[i]);
171 // cout << "unrecovable error\n";
172 printf("unrecovable error\n");
173 break;
174 }
175 }
176 else if( recvNum == 0)
177 {
178 // 這裡表示對端的socket已正常關閉.發送過FIN了。
179 CloseAndDisable(sockfd, events[i]);
180
181 printf("counterpart has shut off\n");
182 break;
183 }
184
185 // recvNum > 0
186 count += recvNum;
187 if ( recvNum == MAXLINE)
188 {
189 continue; // 需要再次讀取
190 }
191 else // 0 < recvNum < MAXLINE
192 {
193 // 安全讀完
194 bReadOk = true;
195 break; // 退出while(1),表示已經全部讀完數據
196 }
197 }
198
199 if (bReadOk == true)
200 {
201 // 安全讀完了數據
202 line[count] = '\0';
203
204 // cout << "we have read from the client : " << line;
205 printf("we have read from the client : %s",line);
206 //設置用於寫操作的文件描述符
207
208 ev.data.fd=sockfd;
209 //設置用於注測的寫操作事件
210
211 ev.events = EPOLLOUT | EPOLLET;
212 //修改sockfd上要處理的事件為EPOLLOUT
213
214 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
215 }
216 }
217 else if(events[i].events & EPOLLOUT) // 如果有數據發送
218 {
219 const char str[100] = "hello from epoll : this is a long string which may be cut by the net\n";
220 int strlen = sizeof(str);
221 printf("Write %s\n",str);
222 sockfd = events[i].data.fd;
223
224 bool bWritten = false;
225 int writenLen = 0;
226 int count = 0;
227 char * head = line;
228 while(1)
229 {
230 // 確保sockfd是非阻塞的
231 writenLen = send(sockfd, str + count, strlen - count/*MAXLINE*/, 0);
232 if (writenLen == -1)
233 {
234 if (errno == EAGAIN)
235 {
236 // 對於nonblocking 的socket而言,這裡說明了已經全部發送成功了
237 bWritten = true;
238 break;
239 }
240 else if(errno == ECONNRESET)
241 {
242 // 對端重置,對方發送了RST
243 CloseAndDisable(sockfd, events[i]);
244 printf("counterpart send out RST\n");
245 break;
246 }
247 else if (errno == EINTR)
248 {
249 // 被信號中斷
250 continue;
251 }
252 else
253 {
254 // 其他錯誤
255 }
256 }
257
258 if (writenLen == 0)
259 {
260 // 這裡表示對端的socket已正常關閉.
261 CloseAndDisable(sockfd, events[i]);
262
263 printf("counterpart has shut off\n");
264 break;
265 }
266
267 // 以下的情況是writenLen > 0
268 count += writenLen;
269 if(count < strlen)
270 {
271 // 可能還沒有寫完
272 continue;
273 }
274 else // 0 < writenLen < MAXLINE
275 {
276 // 已經寫完了
277 bWritten = true;
278 break; // 退出while(1)
279 }
280 }
281
282 if (bWritten == true)
283 {
284 //設置用於讀操作的文件描述符
285 ev.data.fd=sockfd;
286
287 //設置用於注測的讀操作事件
288 ev.events=EPOLLIN | EPOLLET;
289
290 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
291 }
292 }
293 }
294 }
295 return 0;
296 }
2.客戶端
1 #include <sys/socket.h>
2 #include <sys/epoll.h>
3 #include <netinet/in.h>
4 #include <arpa/inet.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <stdio.h>
8 #include <errno.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <pthread.h>
12
13
14
15
16 #define bool int
17 #define false 0
18 #define true 1
19
20
21 #define MAXLINE 100
22 #define OPEN_MAX 100
23 #define LISTENQ 20
24 #define SERV_PORT 5000
25 #define INFTIM 1000
26
27
28
29
30 int sockfd;
31 char recvline[MAXLINE + 1] = "\0";
32 pthread_t threadid;
33
34 void *m_thread(){
35 int len=0;
36 while(1){
37 len = recv(sockfd,recvline,30,0);
38 recvline[len] = '\0';
39 printf("%s",recvline);
40 if(len == -1)
41 return;
42 }
43 }
44
45 int
46 main(int argc, char **argv)
47 {
48
49
50 struct sockaddr_in servaddr;
51
52 if (argc != 2){
53 printf("usage: a.out <IPaddress>\n");
54 return 0;
55 }
56
57 if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
58
59 printf("socket error\n");
60 return 0;
61 }
62
63
64
65
66 bzero(&servaddr, sizeof(servaddr));
67 servaddr.sin_family = AF_INET;
68 servaddr.sin_port = htons(5000);
69 if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
70 printf("inet_pton error for %s\n", argv[1]);
71
72 if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
73 printf("connect error\n");
74 if(pthread_create(&threadid, NULL, m_thread, NULL) != 0)
75 perror("create Thread:");
76
77 char input[100];
78 while (fgets(input, 100, stdin) != EOF)
79 {
80 write(sockfd, input, strlen(input));
81
82 int n = 0;
83 int count = 0;
84
85 }
86 exit(0);
87 }
參考:
blog.csdn.net/ljx0305/article/details/4065058
http://www.cnblogs.com/aicro/archive/2012/12/27/2836170.html
http://blog.csdn.net/dodo_check/article/details/8553265