epoll知识点迅速理解

这里的解说都是为最下面一个完整例子准备的。参考资料:http://jazka.blog.51cto.com/809003/252620/


如果不了解socket编程的话请先了解一个socket编程


epoll的2种工作方式:LT和ET。
LT(level triggered)是缺省的工作方式,同时支持block和non-block。其实这个有点像电路里面的电平触发方式。在这种模式下,内核会告诉你一个文件描述符fd就绪了,然后你就可以对这个fd进行IO操作。如果你不做任何操作,内核会继续通知你。所以,假如你读取数据没有读取完时,内核会继续通知你。其实传统的select/poll就是这种模式。
ET(edge-triggered)是告诉工作方式,只支持non-block。这个最好

然后对这个fd只通知你一次,因为之后一直为就绪态,没有了状态的变化,直到你做了某些操作导致了那个fd不再为就绪态。但是注意,如果一直不对这个fd进行IO操作,内核不会发送更多的通知。

 

epoll主要用到三个函数epoll_create,epoll_ctl,epoll_wait。

int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数第一个参数是
epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;

 

第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:

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队列里

 


int epoll_wait(int epfd, struct epoll_event * events,

int maxevents, int timeout);
等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

 

 socklen_t
 这个不知道是什么类型,我只觉得它和int 有点像,用来表示长度的
 例:socklen_t length=sizeof(**);
 它包含在头文件: #include <netinet/in.h>中
 原文:http://hi.baidu.com/dlpucat/blog/item/bb2b50c49fa4f0c839db49a3.html

 还有 http://www.v108.cn/jty/2008-3-2/777.htm 中也涉及到了这个 socklen_t
 不过它用的头文件是     #include <netdb.h>        #include <sys/socket.h>

 

要注意的是:

epoll可以监听多个fd,每当fd有动静就生成一个事件放进一个你定义的struct epoll_event events[??]数组,由你遍历它


下面代码较难理解的是这个循环:

for(;;)

{

nfds = epoll_wait(epfd, events, 32, 10000);//等待事件发生

.....//是否有错误?

for(i=0; i<nfds; ++i)


        if(events[i].data.fd == listenfd)
        {有动静?先accept吧,把accept返回的句柄加入epoll监听}
        else if(events[i].events & EPOLLIN){如果event的状态为输入,说明有数据输入,把它拷贝出来,并修改把epoll设置为监听socket输出(此时会触发EPOLLOUT事件)}
        else if(events[i].events & EPOLLOUT) {如果event的状态为可以输出,说明缓冲区不满,可以输出,输出,并修改把epoll设置为监听socket输入}

}

}

错误处理:

 close(sockfd);  

close(epfd);


 

下面给出epoll编写服务器的模型:                修正了错误    另开一个shell 输入wget 127.0.0.1即有反应

#include <stdio.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define MAXSIZE         64000
#define MAXEPS            256
//#define EVENTS            100
#define LISTENQ         32
#define SERV_PORT     80

int setnonblock(int sock)
{
    int flags = fcntl(sock, F_GETFL, 0);

    if(-1 == flags)
    {
        perror("fcntl(sock, F_GETFL)");
        return -1;
    }

    flags |= O_NONBLOCK;

    if(-1 == fcntl(sock, F_SETFL, flags))
    {
        perror("fcntl(sock, F_SETFT, flags)");
        return -2;
    }

    return 0;
}

int main(int argc, char *argv[])
{
    int i, maxi, listenfd, connfd, sockfd, epfd, nfds;
    ssize_t n;

    char buf[MAXSIZE];

    socklen_t clilen;

    struct epoll_event ev, events[32];

    epfd = epoll_create(MAXEPS);

    struct sockaddr_in clientaddr;
    struct sockaddr_in serveraddr;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    setnonblock(listenfd);

    ev.data.fd = listenfd;

    ev.events = EPOLLIN | EPOLLET;

    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);

    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;

    /*
          char *local_addr = "10.0.2.15";
          inet_aton(local_addr, &(serveraddr.sin_addr));
     */
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(SERV_PORT);

    if(bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) != 0)
    {
        perror("bind failed");
        return -1;
    }

    if(listen(listenfd, LISTENQ) != 0)
    {
        perror("listen failed");
        return -2;
    }

    maxi = 0;

    printf("began to accept...\n");

    for(;;)
    {
        nfds = epoll_wait(epfd, events, 32, -1);

        if(-1 == nfds)
        {
            if(EINTR == errno)
            {
                continue;
            }
            return -1;
        }

        for(i = 0; i < nfds; ++i)
        {
            if(events[i].data.fd == listenfd)
            {
                connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);

                if(connfd < 0)
                {
                    perror("accept failed");
                    return -3;
                }

                printf("accepted..\n");

                setnonblock(connfd);

                ev.data.fd = connfd;
                ev.events = EPOLLIN | EPOLLET;

                epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
            }
            else if(events[i].events & EPOLLIN)
            {
                if((sockfd = events[i].data.fd) < 0)
                    continue;

                if((n = read(sockfd, buf, MAXSIZE)) < 0)
                {
                    if(errno == ECONNRESET)
                    {
                        close(sockfd);
                        events[i].data.fd = -1;
                    }
                    else
                    {
                        perror("read failed");
                    }
                }
                else if(0 == n)
                {
                    close(sockfd);
                    events[i].data.fd = -1;
                }

                printf("Read the buf: %s\n", buf);

                ev.data.fd = sockfd;
                ev.events = EPOLLOUT | EPOLLET;

                epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
            }
            else if(events[i].events & EPOLLOUT)
            {
                sockfd = events[i].data.fd;
                char *sndbuf = "I get your message!";
                write(sockfd, sndbuf, 10);

                ev.data.fd = sockfd;
                ev.events = EPOLLIN | EPOLLET;

                epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev);
            }
        }
    }


    close(epfd);
    close(sockfd);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值