EPOLLONESHOT事件

2014-11-23 23:30:16 · 作者: · 浏览: 6
epoll模式中事件可能被触发多次,比如socket接收到数据交给一个线程处理数据,在数据没有处理完之前又有新数据达到触发了事件,另一个线程被激活获得该socket,从而产生多个线程操作同一socket,即使在ET模式下也有可能出现这种情况。采用EPOLLONETSHOT事件的文件描述符上的注册事件只触发一次,要想重新注册事件则需要调用epoll_ctl重置文件描述符上的事件,这样前面的socket就不会出现竞态。
一个采用EPOLLONETSHOT的例子:
epoll_oneshot._server.cpp服务端程序
[cpp] view plaincopy
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#define MAX_EVENT_NUMBER 1024//最大事件连接数  
#define BUFFER_SIZE 1024//接收缓冲区大小  
using namespace std;  
struct fds{//文件描述符结构体,用作传递给子线程的参数  
    int epollfd;  
    int sockfd;  
};  
int setnonblocking(int fd){//设置文件描述符为非阻塞  
    int old_option=fcntl(fd,F_GETFL);  
    int new_option=old_option|O_NONBLOCK;  
    fcntl(fd,F_SETFL,new_option);  
    return old_option;  
}  
void addfd(int epollfd,int fd,bool oneshot){//为文件描述符添加事件  
    epoll_event event;  
    event.data.fd=fd;  
    event.events=EPOLLIN|EPOLLET;  
    if(oneshot){//采用EPOLLONETSHOT事件  
        event.events|=EPOLLONESHOT;  
    }  
    epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);  
    setnonblocking(fd);  
}  
void reset_oneshot(int epollfd,int fd){//重置事件  
    epoll_event event;  
    event.data.fd=fd;  
    event.events=EPOLLIN|EPOLLET|EPOLLONESHOT;  
    epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);  
}  
void* worker(void* arg){//工作者线程(子线程)接收socket上的数据并重置事件  
    int sockfd=((fds*)arg)->sockfd;  
    int epollfd=((fds*)arg)->epollfd;//事件表描述符从arg参数(结构体fds)得来  
    cout<<"start new thread to receive data on fd:"<
=0); ret=bind(listenfd,(struct sockaddr*)&address,sizeof(address)); assert(ret!=-1); ret=listen(listenfd,5); assert(ret!=-1); epoll_event events[MAX_EVENT_NUMBER]; int epollfd=epoll_create(5); assert(epollfd!=-1); addfd(epollfd,listenfd,false);//不能将监听端口listenfd设置为EPOLLONESHOT否则会丢失客户连接 while(1){ int ret=epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);//等待事件发生 if(ret<0){ cout<<"epoll error"<

epoll_oneshot_client.cpp客户端程序
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#define BUFFER_SIZE 512  
using namespace std;  
int main(int argc,char* argv[]){  
    if(argc<=2){  
        cout<<"argc<=2"<=0);  
    int fd=open("epoll_oneshot_server.cpp",O_RDONLY);//打开一个文件并向服务端不断发送数据  
    if(connect(sockfd,(struct sockaddr*)&server_address,sizeof(server_address))<0){  
        cout<<"connect error "<0){  
            send(sockfd,buf,strlen(buf),0);  
        }  
    }  
    close(sockfd);  
    return 0;  
}