IO复用:Epoll

Epoll

接口

1.创建文件句柄
int epoll_create(int size);

2.向epoll对象修改.添加.删除事件.
int epoll_ctl(int epfd,int op,int fd,struct epoll_event*event);

events取值:
EPOLLIN 表示数据可以读出(接受.关闭连接)
EPOLLOUT 表示连接可以写入数据发送(向服务器发起连接.连接成功事件)
EPOLLERR 表示对应连接发送错误
EPOLLHUP 表示对应的连接被挂起
op取值:
EPOLL_CTL_ADD 添加新的事件到epoll中
EPOLL_CTL_MOD 修改EPOLL中的事件
EPOLL_CTL_DEL 删除epoll中的事件

3.收集在epoll监控中的事件已经发生的事件
int epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout);

epfd:epoll的描述符.为epoll_create函数创建.
events:则是分配好的epoll_event结构体数组.epoll将会把发生的事件复制到events数组中(events不可以是空指针.内核只负责把数据复制到这个events数组中.不会去帮助我们在用户态中分配内存.内核这种做法效率很高).
maxevents:本次可以返回的最大事件数目.通常maxevents参数与预分配的events数组的大小是相等的.
timeout:表示在没有检测到事件发生时最多等待的时间(单位为毫秒).如果timeout为0.立刻返回.不会等待.-1表示无限期阻塞.

epoll关键结构

struct epoll_event{
    _uint32_t events;
    epoll_data_t data;
}
typedef union epoll_data{
    void*ptr;
    int fd; //描述符
    uint32_t u32;
    uint64_t u64;
}

边缘触发与水平触发

Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时.epoll_wait()会通知处理程序去读写.如果这次没有把数据一次性全部读写完(如读写缓冲区太小).那么下次调用 epoll_wait()时.它还会通知你在上没读写完的文件描述符上继续读写.当然如果你一直不去读写.它会一直通知你.如果系统中有大量你不需要读写的就绪文件描述符.而它们每次都会返回.这样会大大降低处理程序检索自己关心的就绪文件描述符的效率.

水平触发需要做更多的事情

Edge_triggered(边缘触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!
设置方式: stat->_ev.events = EPOLLIN | EPOLLET

设置事件的时候加上|EPOLLET

Epoll_demo

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


#define SERVER_PORT 9000
#define SERVER_IP "127.0.0.1"
#define MAX_FD  10
extern struct epoll_event ev;
struct epoll_event ev;

int create_sockfd();
void epoll_add(int epfd,int fd);
void epoll_del(int epfd,int fd);
void epoll_mod(int epfd,int fd);

int main(void){
    
    
   
    char buff[256];
    int sockfd = create_sockfd(); 
    int epfd = epoll_create(MAX_FD);
    //设置结构体成员
    ev.events=EPOLLIN;
    ev.data.fd =sockfd;
    epoll_add(epfd,sockfd);
    struct epoll_event events[MAX_FD];//接收请求事件
    printf("等待客户端连接!\n");
    while (1)
    {
    
    
        int nread;
        /* printf("server waiting\n");*/
        int n = epoll_wait(epfd,events,MAX_FD,5000);//设置的超时时间为五秒
        if(n==0){
    
    //表示没有事件发生
            printf("time out\n");
        } else if(n==-1){
    
    //出错
            perror("epoll_wait error!\n");
        } else{
    
    
            int i=0;
            for(;i<n;i++){
    
    
                int fd =events[i].data.fd;
                if(fd==-1){
    
    
                    continue;//如果失败则跳过当前
                }
                if(fd==sockfd){
    
    //如果是服务器描述符.则表示客户端请求连接
                             struct sockaddr_in client_addr;
                             memset(&client_addr,'\0',sizeof(client_addr));
                             socklen_t len = sizeof(client_addr);
                             int client_sock=accept(sockfd,(struct sockaddr*)&client_addr,&len);
                             if(client_sock<0){
    
    
                                 continue;
                             }
                             printf("client fd:%d.\n",client_sock);
                             epoll_add(epfd,client_sock);//client_sock添加到内核事件表中
                        }
                else {
    
    
                        if(events[i].events&EPOLLIN){
    
    //如果为已经就绪的事件且为epollin
                                nread=read(fd,buff,sizeof(buff)-1);
                                buff[nread]='\0';
                                if(nread==0){
    
    //表示客户端断开连接
                                /* 
							    ** 这里不能先close,应该先调用epoll_del,
							    ** 因为先调用close关闭了文件描述符后,再调用epoll_del
							    ** 内核将不能找到所要从内核事件表中移除的文件描述符
							    **/
                                    printf("removing fd:%d\n",fd);
                                    epoll_del(epfd,fd);//将内核事件表中fd删除
                                    close(fd);
                                }else{
    
    //客户端数据请求
                                    printf("serving client on fd %d, read:%s", fd, buff);
                                    ev.events=EPOLLOUT;
                                    ev.data.fd=fd;
                                    epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
                                    for(i=0; i<nread; i++){
    
    
                                        /*if(buf[i]>='a' && buf[i]<='z'){
                                            buf[i] = buf[i] - 32;
                                        }*/
                                        buff[i] = toupper(buff[i]);
                                    }
                                  
                                }
                        }else if(events[i].events&EPOLLOUT){
    
    
                                write(fd,buff,strlen(buff));
                                printf("serving send on fd %d, send:%s", fd, buff);
                                ev.events =EPOLLIN;
                                ev.data.fd=fd;
                                epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
                        }
                }
           }
        }
    }
    
    return 0;
}

void epoll_add(int epfd,int fd){
    
        //添加新的事件以及描述符到内核事件表
    ev.events=EPOLLIN;
    ev.data.fd =fd;
    if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev)==-1){
    
    
        perror("epoll_ctl_add error!\n");
    }

}
void epoll_del(int epfd,int fd){
    
        //删除事件以及描述符
    if(epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL)==-1){
    
    
        perror("epoll_ctl_del error!\n");
    }
}
int create_sockfd(){
    
    
    int ret;
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if (sockfd == -1)
	{
    
    
		return -1;
	}
    struct sockaddr_in server_addr;
    memset(&server_addr,'\0',sizeof(server_addr));
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(SERVER_PORT);
    server_addr.sin_addr.s_addr=inet_addr(SERVER_IP);
    if(ret=bind(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1){
    
    
        return -1;
    }
    listen(sockfd,5);
    return sockfd;
}

猜你喜欢

转载自blog.csdn.net/qq_43507406/article/details/113944953