epoll一点思考(4)

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 10000

int 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 10000

int 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都用的它。











                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值