前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >epoll入门

epoll入门

作者头像
李海彬
发布2018-03-22 14:44:52
7970
发布2018-03-22 14:44:52
举报
文章被收录于专栏:Golang语言社区Golang语言社区

epoll用到的所有函数都是在头文件sys/epoll.h中声明的,下面简要说明所用到的数据结构和函数: 所用到的数据结构

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 */ 
        };  

结构体epoll_event 被用于注册所感兴趣的事件和回传所发生待处理的事件,其中epoll_data 联合体用来保存触发事件的某个文件描述符相关的数据,例如一个client连接到服务器,服务器通过调用accept函数可以得到于这个client对应的socket文件描述符,可以把这文件描述符赋给epoll_data的fd字段以便后面的读写操作在这个文件描述符上进行。epoll_event 结构体的events字段是表示感兴趣的事件和被触发的事件可能的取值为:

  1. EPOLLIN :表示对应的文件描述符可以读;
  2. EPOLLOUT:表示对应的文件描述符可以写;
  3. EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
  4. EPOLLERR:表示对应的文件描述符发生错误;
  5. EPOLLHUP:表示对应的文件描述符被挂断;
  6. EPOLLET:表示对应的文件描述符有事件发生;

所用到的函数: 1、epoll_create函数 函数声明:int epoll_create(int size) 该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围(我觉得这个参数和select函数的第一个参数应该是类似的但是该怎么设置才好,我也不太清楚)。 2、epoll_ctl函数 函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。 参数:epfd:由 epoll_create 生成的epoll专用的文件描述符; op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除 fd:关联的文件描述符; event:指向epoll_event的指针; 如果调用成功返回0,不成功返回-1 3、epoll_wait函数 函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout) 该函数用于轮询I/O事件的发生; 参数: epfd:由epoll_create 生成的epoll专用的文件描述符; epoll_event:用于回传代处理事件的数组; maxevents:每次能处理的事件数; timeout:等待I/O事件发生的超时值; 返回发生事件数。

以代码来说明问题: 首先给出server的代码,需要说明的是每次accept的连接,加入可读集的时候采用的都是LT模式,而且接收缓冲区是5字节的,也就是每次只接收5字节的数据:

#include<iostream>  
#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>  
using namespace std;  
 
#define MAXLINE  5  
#define OPEN_MAX  100  
#define LISTENQ  20  
#define SERV_PORT  5000  
#define INFTIM  1000  
 
void setnonblocking(int sock)  
{  
 int opts;  
    opts = fcntl(sock,F_GETFL);  
 if(opts < 0)  
    {  
        perror(" fcntl(sock,GETFL)");  
        exit (1);  
    }  
    opts = opts|O_NONBLOCK;  
 if(fcntl(sock,F_SETFL,opts) < 0)  
    {  
        perror("fcntl(sock,SETFL,opts)");  
        exit (1);  
    }     
}  
 
int main()  
{  
 int i, maxi, listenfd, connfd, sockfd, epfd, nfds;  
    ssize_t n;  
 char line[MAXLINE];  
    socklen_t clilen;  
 
 // 声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件 
 struct epoll_event ev,events[20];  
 
 // 生成用于处理accept的epoll专用的文件描述符 
    epfd = epoll_create(256);  
 
 struct sockaddr_in clientaddr;  
 struct sockaddr_in serveraddr;  
    listenfd = socket(AF_INET, SOCK_STREAM,  0 );  
 // 把socket设置为非阻塞方式 
     setnonblocking(listenfd);  
 
 // 设置与要处理的事件相关的文件描述符 
    ev.data.fd = listenfd;  
 
 // 设置要处理的事件类型 
    ev.events = EPOLLIN;  
 // 注册epoll事件 
    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);  
 
    bzero(&serveraddr, sizeof(serveraddr));  
    serveraddr.sin_family  =  AF_INET;  
 char *local_addr = "127.0.0.1";  
    inet_aton(local_addr, &(serveraddr.sin_addr)); // htons(SERV_PORT); 
    serveraddr.sin_port = htons(SERV_PORT);  
    bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));  
    listen(listenfd, LISTENQ);  
    maxi = 0;  
 for(;;)   
    {  
 // 等待epoll事件的发生 
        nfds = epoll_wait(epfd, events, 20 , 500);  
 // 处理所发生的所有事件      
 for(i = 0 ;i < nfds; ++ i)  
        {  
 if(events[i].data.fd == listenfd)  
            {                 
                clilen=sizeof(struct sockaddr);   
                connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clilen);  
 if (connfd < 0 )  
                {  
                    perror("connfd<0");  
                    exit ( 1 );  
                }  
 // setnonblocking(connfd); 
 char *str = inet_ntoa(clientaddr.sin_addr);  
                cout  <<   " accapt a connection from  "   <<  str  <<  endl;  
 
 // 设置用于读操作的文件描述符 
                ev.data.fd = connfd;  
 // 设置用于注测的读操作事件 
                ev.events = EPOLLIN|EPOLLET;  
 // ev.events = EPOLLIN; 
 // 注册ev 
                epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);  
            }  
 else if(events[i].events & EPOLLIN)  
            {  
                cout  <<   " EPOLLIN "   <<  endl;  
 if((sockfd = events[i].data.fd) < 0)   
 continue;  
 if((n = read(sockfd, line, MAXLINE)) < 0)   
                {  
 if(errno == ECONNRESET)   
                    {  
                        close(sockfd);  
                        events[i].data.fd  =   - 1 ;  
                    }    
 else 
                        std::cout << " readline error " << std::endl;  
                }    
 else if  (n  ==   0 )   
                {  
                    close(sockfd);  
                    events[i].data.fd = - 1 ;  
                }  
                line[n] = ' \0';   
                cout<<" read  "<<line<<endl;  
 
 // 设置用于写操作的文件描述符 
                ev.data.fd = sockfd;  
 // 设置用于注测的写操作事件 
                ev.events = EPOLLOUT|EPOLLET;  
 // 修改sockfd上要处理的事件为EPOLLOUT 
 // epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd, & ev); 
            }  
 else if (events[i].events & EPOLLOUT)  
            {     
                sockfd = events[i].data.fd;  
                write(sockfd, line, n);  
 
 // 设置用于读操作的文件描述符 
                ev.data.fd = sockfd;  
 // 设置用于注测的读操作事件 
                ev.events = EPOLLIN|EPOLLET;  
 // 修改sockfd上要处理的事件为EPOLIN 
                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd, & ev);  
            }  
        }  
    }  
 return  0 ;  
}  
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档