epoll一点思考(4)

来源:互联网 发布:js 用 添加数组 编辑:程序博客网 时间:2024/04/29 12:04

epoll EPOLLONESHOT

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里,这样IN事件会被保留。EPOLLONESHOT区别就是,每次IN事件都是串行进行,而不是多个in事件并行。

ET模式与LT模式EPOLLONESHOT处理相同

(1)设置EPOLLONESHOT,创建线程读取,线程中不在将socket放入epoll队列,后续再发数据不触发in事件,无法读取。代码如下:

#include <sys/socket.h>#include <sys/epoll.h>#include <netinet/in.h>#include <arpa/inet.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <errno.h>#include <sys/types.h>#include <sys/mman.h>#include <iostream>using namespace std;#define MAX_EVENTS 10000int epfd = 0;void* process(void* ptr){    int sockfd = *(int*)ptr;    char buf;    while(read(sockfd, &buf, 1) > 0)    {        printf("%c\n",buf);        sleep(5);    }    cout << "thread out " << endl;    //event->events = EPOLLIN | EPOLLET;    //epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, event);}int main(int argc, char **argv){    short port = 6002; // default port       int listenFd = socket(AF_INET, SOCK_STREAM, 0);    int reuse = 1;    setsockopt(listenFd , SOL_SOCKET , SO_REUSEADDR , &reuse , sizeof(reuse));    fcntl(listenFd, F_SETFL, O_NONBLOCK); // 设置非阻塞方式    sockaddr_in sin;    bzero(&sin, sizeof(sin));    sin.sin_family = AF_INET;    sin.sin_addr.s_addr = INADDR_ANY;    sin.sin_port = htons(port);    bind(listenFd, (const sockaddr*) &sin, sizeof(sin));    listen(listenFd, 5);    struct epoll_event ev, events[MAX_EVENTS];    //生成用于处理accept的 epoll专用的文件描述符    epfd = epoll_create(MAX_EVENTS);    //设置与要处理的事件相关的文件描述符    ev.data.fd = listenFd;    //设置要处理的事件类型    ev.events = EPOLLIN | EPOLLET;    //注册epoll事件    if (epoll_ctl(epfd, EPOLL_CTL_ADD, listenFd, &ev) < 0)    {        printf("worker epoll_ctl error = %s.", strerror(errno));        exit(1);    }    while (true)    {        // 等待epoll事件的发生        int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);        // 处理所发生的所有事件        for (int i = 0; i < nfds; ++i) // for循环中可以修改为线程池处理epoll事件        {            if (events[i].data.fd == listenFd)            {                socklen_t clilen;                struct sockaddr_in clientaddr;                int sockfd = accept(listenFd, (sockaddr *) &clientaddr,                        &clilen);                if (sockfd < 0)                {                    continue;                }                // 设置非阻塞                if (fcntl(sockfd, F_SETFL,                        fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK) == -1)                {                    continue;                }                                               ev.data.fd = sockfd;                //设置用于注测的读操作事件                ev.events = EPOLLIN | EPOLLET |EPOLLONESHOT;                //注册ev                epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);            }            else if (events[i].events & EPOLLIN)            {                cout << "epollin--->" << endl;                int sockfd = events[i].data.fd;                pthread_t tid;                pthread_create(&tid, NULL, process, &sockfd);                pthread_detach(tid);                usleep(1000);            }        }    }    return 0;}

(2)设置EPOLLONESHOT,创建线程读取,线程返回前将socket放入epoll队列,后续再次发送数据会触发in事件。代码如下:

/* * main.cpp * *  Created on: 2012-12-27 *      Author: root */#include <sys/socket.h>#include <sys/epoll.h>#include <netinet/in.h>#include <arpa/inet.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <errno.h>#include <sys/types.h>#include <sys/mman.h>#include <iostream>using namespace std;#define MAX_EVENTS 10000int epfd = 0;void* process(void* ptr){    int sockfd = *(int*)ptr;    struct epoll_event ev;    char buf;    while(read(sockfd, &buf, 1) > 0)    {        printf("%c\n",buf);        sleep(5);    }    cout << "thread out " << endl;    ev.data.fd = sockfd;    //设置用于注测的读操作事件    ev.events = EPOLLIN | EPOLLET |EPOLLONESHOT;    //注册ev    epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);   }int main(int argc, char **argv){    short port = 6002; // default port       int listenFd = socket(AF_INET, SOCK_STREAM, 0);    int reuse = 1;    setsockopt(listenFd , SOL_SOCKET , SO_REUSEADDR , &reuse , sizeof(reuse));    fcntl(listenFd, F_SETFL, O_NONBLOCK); // 设置非阻塞方式    sockaddr_in sin;    bzero(&sin, sizeof(sin));    sin.sin_family = AF_INET;    sin.sin_addr.s_addr = INADDR_ANY;    sin.sin_port = htons(port);    bind(listenFd, (const sockaddr*) &sin, sizeof(sin));    listen(listenFd, 5);    struct epoll_event ev, events[MAX_EVENTS];    //生成用于处理accept的 epoll专用的文件描述符    epfd = epoll_create(MAX_EVENTS);    //设置与要处理的事件相关的文件描述符    ev.data.fd = listenFd;    //设置要处理的事件类型    ev.events = EPOLLIN | EPOLLET;    //注册epoll事件    if (epoll_ctl(epfd, EPOLL_CTL_ADD, listenFd, &ev) < 0)    {        printf("worker epoll_ctl error = %s.", strerror(errno));        exit(1);    }    while (true)    {        // 等待epoll事件的发生        int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);        // 处理所发生的所有事件        for (int i = 0; i < nfds; ++i) // for循环中可以修改为线程池处理epoll事件        {            if (events[i].data.fd == listenFd)            {                socklen_t clilen;                struct sockaddr_in clientaddr;                int sockfd = accept(listenFd, (sockaddr *) &clientaddr,                        &clilen);                if (sockfd < 0)                {                    continue;                }                // 设置非阻塞                if (fcntl(sockfd, F_SETFL,                        fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK) == -1)                {                    continue;                }                                               ev.data.fd = sockfd;                //设置用于注测的读操作事件                ev.events = EPOLLIN | EPOLLET |EPOLLONESHOT;                //注册ev                epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);            }            else if (events[i].events & EPOLLIN)            {                cout << "epollin--->" << endl;                int sockfd = events[i].data.fd;                pthread_t tid;                pthread_create(&tid, NULL, process, &sockfd);                pthread_detach(tid);                usleep(1000);            }        }    }    return 0;}

(3)设置EPOLLONESHOT,创建线程读取,线程返回前,sleep几秒,在这几秒内,在将socket放入epoll队列前,发送数据,后续再次发送数据会触发in事件。

备忘:

边界触发的效率高,但程序实现上要小心,漏掉没处理的不会得到提醒了。
而电平触发的程序实现上方便,效率要低些,似乎libevent和lighttpd都用的它。