linux I/O复用--------epoll

epoll是linux特有的I/O复用函数。它在实现和使用上与select,poll有很大的差异。首先,epoll使用一组函数来完成任务,而不只是单个函数,其次,epoll把用户关心的文件描述符上的事件都放在内核里的一个时间表中,而无需像select和epoll那样每次调用都要重复传入文件描述符集或事件集,但epoll需要使用一个额外的文件描述符来唯一标识内核中的这个时间表,这个文件描述符使用如下epoll_create函数来创建。

int epoll_create(int size);用于创建一个文件描述符来唯一标识内核中的事件表。size不起作用,他只是告诉内核事件表需要多大。

对事件表的操作:

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

epfd:是int epoll_create();函数的返回值(标识内核事件表的文件描述符)。

Op:指定操作类型:

            1:EPOLL_CTL_ADD:往内核事件表上注册fd的事件

            2:EPOLL_CTL_MOD:修改内核时间表上的事件

            3:EPOLL_CTL_DEL:删除内核事件表上的事件

Struct epoll_event* event:此处的event只是一个结构体变量

Struct epoll_event

{

          _uint32_t event;  //epoll事件

         Epoll_data_t data: //用户数据

}

Typedef union epoll_data

{

        Void* ptr;

        Int fd;   //关注的事件类型

}epoll_data_t;

epoll_wait():在一段时间内等待一组文件描述符上的事件

Int epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout);  成功时返回就绪文件描述符的个数  失败返回-1

Epfd:是int epoll_create();函数的返回值(标识内核事件表的文件描述符)。

struct epoll_event* events:events是struct epoll_event类型的一个数组(存在于用户空间,用于输出epoll_wait()检测到的就绪事件)

maxevents:数组的大小(指定监听多少个事件)

timeout:超时时间,以毫秒记

epoll_wait函数如果检测到事件,就将所有有序的事件从内核事件表中复制到它的第二个参数events指向的数组中,这个数组只用于输出epoll_wait检测到的就绪事件,而不像select和poll的数组参数那样即用于传入用户注册的事件,又用于输出内核检测到的就绪事件,这要就极大地提高了索引就绪文件描述符的效率。

一幅图可能能让大家更好的理解epoll带给我们的方便。

epoll

epoll的优点:

1:用户关注的文件描述符记录在内核事件表中,无需两次拷贝(实际有一次拷贝,就是epoll_wait()函数检测到事件,就将就绪事件从内核事件表中拷贝到用户空间events结构体数组中,但是这次拷贝很小几乎可以忽略不计)

2:关注的文件描述符的个数以及大小受系统限制(系统有规定一个进程所能打开的文件描述符的个数)

3:返回的就是就绪文件描述符,所以检索文件描述符的时间复杂度为O(1)

epoll的缺点:

它的缺点就是它关注的文件描述符很少,就绪的文件描述符很多时,虽然它不需要轮询检测但是它调用回调函数也需要消耗栈帧空间,

epoll的 客户端和服务器发送数据的代码:

/********************客户端************************/
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>

void main()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd!=-1);

    struct sockaddr_in ser,cli;
    ser.sin_family=AF_INET;
    ser.sin_port=htons(6000);
    ser.sin_addr.s_addr=inet_addr("127.0.0.1");

    
    int n=connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));
    assert(n!=-1);

    while(1)
    {
        char buff[128]={0};
        printf("input:\n");
        fgets(buff,128,stdin);
        
        if(strncmp(buff,"end",3)==0)
        {
            close(n);
            break;
        }
        send(sockfd,buff,strlen(buff)-1,0);
        memset(buff,0,128);
        recv(sockfd,buff,127,0);
        printf("%s\n",buff);
    }
    close(sockfd);
}
/*************服务器***************/
#define _GNU_SOURCE 1
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<assert.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<errno.h>
#include<sys/select.h>
#include<sys/epoll.h>
#define MAX 1024


void main()
{
    int listenfd=socket(AF_INET,SOCK_STREAM,0);
    assert(listenfd!=-1);
    struct sockaddr_in ser,cli;
    ser.sin_family=AF_INET;
    ser.sin_port=htons(6000);
    ser.sin_addr.s_addr=inet_addr("127.0.0.1");



    int res=bind(listenfd,(struct sockaddr*)&ser,sizeof(ser));
    assert(res!=-1);

    listen(listenfd,5);

    struct epoll_event event;
    int epfd=epoll_create(5);

    event.data.fd=listenfd;
    event.events=EPOLLIN;
    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&event);
    struct epoll_event events[MAX];

    while(1)
    {
         int n=epoll_wait(epfd,events,MAX,-1);
         printf("epoll_wait return\n");
         if(n==-1)
         {
             printf("epoll error\n");
             exit(0);
         }
         else
         {
             int i=0;
             for(;i<n;++i)
             {
                int fd=events[i].data.fd;
    
                if(fd==listenfd)
                {
                    int len=sizeof(cli);
                    int c=accept(listenfd,(struct sockaddr*)&cli,&len);
                    if(c<0)
                    {
                        printf("one client link error\n");
                        continue;
                    }
                    printf("one client link\n");
                    event.data.fd=c;
                    event.events=EPOLLIN|EPOLLRDHUP;
                    epoll_ctl(epfd,EPOLL_CTL_ADD,c,&event);
                }
                    
                else
                {

                    if(events[i].events&EPOLLRDHUP)
                    {
                        close(fd);
                        epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&event);
                        printf("events error\n");
                        continue;
                    }
                    if(events[i].events&EPOLLIN)
                    {
                        while(1)
                        {

                            char buff[128]={0};
                            int n=recv(fd,buff,127,0);
                            if(n<=0)
                            {
                                printf("not get\n");
                                close(fd);
                                epoll_ctl(epfd,EPOLL_CTL_DEL,fd,&event);
                                continue;
                            }   
                            printf("%d  %s\n",fd,buff);
                            send(fd,"ok",2,0);
                        }
                    }
                }
             }
         }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_41537785/article/details/81170601