epoll是Kernel 2.6后新加入的事件機制,在高并發(fā)條件下,遠優(yōu)于select.
用個硬件中的例子吧,可能不太恰當:epoll相當于I/O中斷(有的時候才相應(yīng)),而select相當于輪詢(總要反復查詢)。
其實epoll比slect好用很多,主要一下幾個用法。
struct epoll_event ; epoll事件體,事件發(fā)生時候你可以得到一個它。其中epoll_event.data.fd可以存儲關(guān)聯(lián)的句柄,epoll_event.event 是監(jiān)聽標志,常用的有EPOLLIN (有數(shù)據(jù),可以讀)、EPOLLOUT(有數(shù)據(jù),可以寫)EPOLLET(有事件,通用);
(1)創(chuàng)建epoll句柄
int epFd = epoll_create(EPOLL_SIZE);
(2)加入一個句柄到epoll的監(jiān)聽隊列
ev.data.fd = serverFd; ev.events = EPOLLIN | EPOLLET; epoll_ctl(epFd, EPOLL_CTL_ADD, serverFd, &ev);
上面的fd是你要綁定給事件發(fā)生時候使用的fd,到時候只能操作這個,下面是事件類型。
使用epoll_ctl添加到之中,EPOLL_CTL_ADD是epoll控制類型,這里監(jiān)聽的fd和給event的fd一般相同。
(3)等待event返回
int nfds = epoll_wait(epFd, evs, EVENT_ARR, -1);
傳入的evs是epoll_event的數(shù)組,EVENT_ARR應(yīng)當是不超過這個數(shù)組的長度。返回nfds的是不超過EVENT_ARR的數(shù)值,表示本次等待到了幾個事件。
(4)遍歷事件
注意,這里遍歷的事件是肯定已經(jīng)發(fā)生了的,而select中遍歷的是每個fd,而fd不一定在FDSET中(即不一定有讀事件發(fā)生)!這是效率最大的差別所在!
for (int i = 0; i < nfds; i++)
{
//do something
}
(5)其他技巧
對事件是否是serverFd判斷,如果是,進行accept并加入epoll監(jiān)聽隊列,要設(shè)置異步讀取。
如果evts[i]&EPOLLIN,表示可讀,使用read進行試探,如果>0表示連接沒有關(guān)閉,否則連接已經(jīng)關(guān)閉(出發(fā)事件又讀取不到東西,表示socket關(guān)閉!)。如果<0出錯。如果>0,需要繼續(xù)讀取直到為0,但是注意這里的為0是在第一次read不為0的前提下,畢竟我們設(shè)置了異步讀取,暫時沒有數(shù)據(jù)可以讀就返回0了!而如果第一次返回0,那么就是關(guān)閉吧!
注意關(guān)閉要移出出epoll并且close(clientFd)
羅嗦了好多,看代碼! 查看源代碼 打印幫助 001 /* 002 * main.cc 003 * 004 * Created on: 2009-11-30 005 * Author: liheyuan 006 * Describe: epoll實現(xiàn)阻塞模式服務(wù)器(Echo服務(wù)器) 007 * 008 * Last Date: 2009-11-30 009 * CopyRight: 2009 @ ICT LiHeyuan 010 */ 011 #include <stdio.h> 012 #include <stdlib.h> 013 #include <unistd.h> 014 #include <fcntl.h> 015 #include <arpa/inet.h> 016 #include <netinet/in.h> 017 #include <sys/epoll.h> 018 #include <errno.h> 019 020 #define EPOLL_SIZE 10 021 #define EVENT_ARR 20 022 #define BACK_QUEUE 10 023 #define PORT 18001 024 #define BUF_SIZE 16 025 026 void setnonblocking(int sockFd) { 027 int opt; 028 029 //獲取sock原來的flag 030 opt = fcntl(sockFd, F_GETFL); 031 if (opt < 0) { 032 printf("fcntl(F_GETFL) fail."); 033 exit(-1); 034 } 035 036 //設(shè)置新的flag,非阻塞 037 opt |= O_NONBLOCK; 038 if (fcntl(sockFd, F_SETFL, opt) < 0) { 039 printf("fcntl(F_SETFL) fail."); 040 exit(-1); 041 } 042 } 043 044 int main() { 045 046 int serverFd; 047 048 //創(chuàng)建服務(wù)器fd 049 serverFd = socket(AF_INET, SOCK_STREAM, 0); 050 setnonblocking(serverFd); 051 052 //創(chuàng)建epoll,并把 serverFd放入監(jiān)聽隊列 053 int epFd = epoll_create(EPOLL_SIZE); 054 struct epoll_event ev, evs[EVENT_ARR]; 055 ev.data.fd = serverFd; 056 ev.events = EPOLLIN | EPOLLET; 057 epoll_ctl(epFd, EPOLL_CTL_ADD, serverFd, &ev); 058 059 //綁定服務(wù)器端口 060 struct sockaddr_in serverAddr; 061 socklen_t serverLen = sizeof(struct sockaddr_in); 062 serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); 063 serverAddr.sin_port = htons(PORT); 064 if (bind(serverFd, (struct sockaddr *) &serverAddr, serverLen)) { 065 printf("bind() fail.\n"); 066 exit(-1); 067 } 068 069 //打開監(jiān)聽 070 if (listen(serverFd, BACK_QUEUE)) { 071 printf("Listen fail.\n"); 072 exit(-1); 073 } 074 075 //死循環(huán)處理 076 int clientFd; 077 sockaddr_in clientAddr; 078 socklen_t clientLen; 079 char buf[BUF_SIZE]; 080 while (1) { 081 //等待epoll事件的到來,最多取EVENT_ARR個事件 082 int nfds = epoll_wait(epFd, evs, EVENT_ARR, -1); 083 //處理事件 084 for (int i = 0; i < nfds; i++) { 085 if (evs[i].data.fd == serverFd && evs[i].data.fd & EPOLLIN) { 086 //如果是serverFd,表明有新連接連入 087 if ((clientFd = accept(serverFd, 088 (struct sockaddr *) &clientAddr, &clientLen)) < 0) { 089 printf("accept fail.\n"); 090 } 091 printf("Connect from %s:%d\n", inet_ntoa(clientAddr.sin_addr), 092 htons(clientAddr.sin_port)); 093 setnonblocking(clientFd); 094 //注冊accept()到的連接 095 ev.data.fd = clientFd; 096 ev.events = EPOLLIN | EPOLLET; 097 epoll_ctl(epFd, EPOLL_CTL_ADD, clientFd, &ev); 098 } else if (evs[i].events & EPOLLIN) { 099 //如果不是serverFd,則是client的可讀 100 if ((clientFd = evs[i].data.fd) > 0) { 101 //先進行試探性讀取 102 int len = read(clientFd, buf, BUF_SIZE); 103 if (len > 0) { 104 //有數(shù)據(jù)可以讀,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 //出發(fā)了EPOLLIN事件,卻沒有可以讀取的,表示斷線 113 printf("Client closed 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("other event.\n"); 127 } 128 } 129 } 130 131 return 0; 132 }
|