epoll编程

包含头文件:

#include <sys/epoll.h>

epoll的接口非常简单,一共就三个函数:
1. int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。

2.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型.

第一个参数是epoll_create()的返回值,

第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD;注册新的fd到epfd中;
EPOLL_CTL_MOD;修改已经注册的fd的监听事件;
EPOLL_CTL_DEL;从epfd中删除一个fd;
第三个参数是需要监听的fd;

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

typedef union epoll_data {
    void *ptr;
    int fd;
    __uint32_t u32;
    __uint64_t u64;
} epoll_data_t;

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

3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待事件的产生,类似于select()调用。

第一个参数:epfd,epoll_create返回的句柄

第二个参数:events用来从内核得到事件的集合

第三个参数:maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,

第四个参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。

该函数返回需要处理的事件数目,如返回0表示已超时。

4.close(epollfd)

退出时记得释放创建的epoll句柄;

5、关于ET、LT两种工作模式:
可以得出这样的结论:
ET模式仅当状态发生变化的时候才获得通知,这里所谓的状态的变化并不包括缓冲区中还有未处理的数据,也就是说,如果要采用ET模式,需要一直read/write直到出错为止,很多人反映为什么采用ET模式只接收了一部分数据就再也得不到通知了,大多因为这样;

而LT模式是只要有数据没有处理就会一直通知下去的.

二者的差异在于:

level-trigger(LT)模式下只要某个socket处于readable/writable状态,无论什么时候进行epoll_wait都会返回该socket;

edge-trigger(ET)模式下只有某个socket从unreadable变为readable或从unwritable变为writable时,epoll_wait才会返回该socket。

所以,在epoll的ET模式下,正确的读写方式为:
读:只要可读,就一直读,直到返回0,或者 errno = EAGAIN
写:只要可写,就一直写,直到数据发送完,或者 errno = EAGAIN

PS:

epoll工作在ET模式的时候,必须使用非阻塞套接口

//设置socket连接为非阻塞模式
void setnonblocking(int sockfd) 
{
    int opts;
  // 得到文件状态标志  
    opts = fcntl(sockfd, F_GETFL, 0);
    if(opts < 0)
    {
        perror("fcntl(F_GETFL)
");
        exit(1);
    }
  // 设置文件状态标志 opts
= (opts | O_NONBLOCK); if(fcntl(sockfd, F_SETFL, opts) < 0) { perror("fcntl(F_SETFL) "); exit(1); } }

6.

参考网址:

epoll详解

http://blog.chinaunix.net/uid-24517549-id-4051156.html

高并发的epoll+线程池,epoll在线程池内

http://blog.chinaunix.net/uid-311680-id-2439723.html

基于epoll实现socket编程完整实例

http://blog.chinaunix.net/uid-20771605-id-4596400.html

epoll使用详解(精髓)

http://blog.csdn.net/ljx0305/article/details/4065058

1)

#define ERR_EXIT(m) do
{
  perror(m);
  exit(EXIT_FAILURE);
} while(0)

//声明epoll_event结构体的变量,event用于注册事件,数组用于回传要处理的事件
struct epoll_event event;
struct epoll_event events[20];
// 生成用于处理accept的epoll专用的文件描述符
int epfd = epoll_create(256);

// 注册监听的事件

memset(&event,0,sizeof(event));

event.data.fd = listenFd;
event.events = EPOLLIN | EPOLLET | EPOLLOUT;

// 注册epoll事件
rlt = epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &event);

if(rlt == -1)
{
  ERR_EXIT("注册事件失败! ");
}


struct timeval outtime;
memset(&outtime, 0x00, sizeof(struct timeval));

const int MAX_EVENTS = 1024; // 最大事件数

void *epoll_loop(void* para)
{
    int nfds; // 临时变量,存放返回值
    int epfd; // 监听用的epoll句柄,epoll_create返回值
    //struct epoll_event events[MAX_EVENTS]; // 监听事件数组
    //struct epoll_event event;
    // struct sockaddr_in client_addr;
    int i;
    int connfd; // 
    for(;;) 
    {
        // 等待epoll事件的发生
        nfds = epoll_wait(epfd,events,MAX_EVENTS,-1); // -1: timeout
        //printf("nfds = %d
", nfds);

        // 处理所发生的所有事件
        if(nfds > 0)
        {
            for(i=0; i<nfds; ++i)
            {
                if(events[i].data.fd == listenfd) // 如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。
                {
                    //while(1)
                    //{
                        // socklen_t cliaddrlen;                         
                        connfd = accept(listenfd,(struct sockaddr *)&client_addr, &cliaddrlen);
                        
                        if(connfd > 0)
                        {
                            //cout << "AcceptThread, accept:" << connfd << ",errno:" << errno << ",connect:" << inet_ntoa(cliaddr.sin_addr) << ":" << ntohs(cliaddr.sin_port) << endl;
                            event.data.fd=connfd;
                            event.events = EPOLLIN | EPOLLET; // ET模式
                            epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&event); // //注册ev.将新的fd添加到epoll的监听队列中

                            //fd_Setnonblocking(event.data.fd);
                            //event.events=EPOLLIN|EPOLLET;
                            //epoll_ctl(epfd,EPOLL_CTL_ADD,event.data.fd,&event);
                        }
                        else
                        {
                            //cout << "AcceptThread, accept:" << connfd << ",errno:" << errno << endl;
                            if (errno == EAGAIN) // 没有连接需要接收了
                            {
                                break;
                            }
                            else if(errno == EINTR) // 可能被中断信号打断,,经过验证对非阻塞socket并未收到此错误,应该可以省掉该步判断
                            {
                                ;
                            }
                            else // 其它情况可以认为该描述字出现错误,应该关闭后重新监听
                            {
                                //...
                            }
                        }
                    //}
                }
                else if(events[i].events & EPOLLIN ) // 接收到数据,读socket
                {
                    n = read(sockfd, line, MAXLINE)) < 0; //
                    
                    ev.data.ptr = md;     //md为自定义类型,添加数据
                    ev.events=EPOLLOUT|EPOLLET;
                    epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改标识符,等待下一个循环时发送数据,异步处理的精髓

                    // 或者如下处理:
                    char recvBuf[1024] = {0}; 
                    int ret = 999;
                    int rs = 1;

                    while(rs)
                    {
                        ret = recv(events[i].data.fd, recvBuf, 1024, 0);// 接受客户端消息
                        if(ret < 0)
                        {
                            // 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读,在这里就当作是该次事件已处理过。
                            if(errno == EAGAIN)
                            {
                                printf("EAGAIN
");
                                break;
                            }
                            else
                            {
                                printf("recv error!
");
                                epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, &event); // 其中 struct epoll_event event;
                                                                                           // memset(&event,0,sizeof(event));
                                                                                           // event.data.fd=listenfd;
                                                                                           // event.events=EPOLLIN|EPOLLET;
                                close(events[i].data.fd);
                                break;
                            }
                        }
                        else if(ret == 0)
                        {
                            // 这里表示对端的socket已正常关闭. 
                            rs = 0;
                        }
                        if(ret == sizeof(recvBuf))
                        {
                            rs = 1; // 需要再次读取
                        }
                        else
                        {
                            rs = 0;
                        }
                    }

                }
                else if(events[i].events & EPOLLOUT) // 有数据待发送,写socket
                {

            struct myepoll_data* md = (myepoll_data*)events[i].data.ptr; //取数据
            sockfd = md->fd;
            send( sockfd, md->ptr, strlen((char*)md->ptr), 0 ); //发送数据
            event.data.fd=sockfd;
            event.events=EPOLLIN|EPOLLET;
            epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改标识符,等待下一个循环时接收数据

//sprintf(buf,"HTTP/1.0 200 OK
Content-type: text/plain

%s","Hello world!
");
                    send(events[i].data.fd,buf,strlen(buf),0);

                    close(events[i].data.fd);
                }
                else
                {
                    close(events[i].data.fd);
                }
                //else if (events[i].events & EPOLLERR || events[i].events & EPOLLHUP) // 有异常发生
                //{
                //    //此时说明该描述字已经出错了,需要重新创建和监听
                //    //close(listenfd);   

                //    // epoll_ctl(epfd, EPOLL_CTL_DEL, listenfd, &event);
                //    // 创建监听socket
                //    //listenfd = socket(AF_INET, SOCK_STREAM, 0);
                //    //...
                //}


                //epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, &event);
                //close(events[i].data.fd);

            }
        }    
    }
}
原文地址:https://www.cnblogs.com/sylar-liang/p/4261388.html