I/O复用----epoll系统调用

I/O复用—-epoll系统调用

#include<sys/epoll.h>//使用epoll的3个函数需加的头文件。

实现:epoll与select,poll有很大的不同,epoll是使用一组函数实现的,而不是某一个函数。
亮点:内核事件表(epfd)就是在内核中建立一个表,专门放事件,底层数据结构是一个红黑树。
数据结构:要知道用了什么数据结构就要先看他涉及到的函数是怎么调用的。实现epoll我们总共涉及到三个函数:
第一个函数

1int  epoll_create(int size);
   int epfd=epoll_create(1);//一般用法

创建内核事件表,返回值为内核事件表的文件描述符。(一般为4)
size参数并不起作用,只是给内核一个提醒告诉它内核事件表的大小,为什么不起作用呢?因为,底层数据结构是红黑树,是动态开辟的,所以给他指定大小并没有什么意义。3个i/o复用系统调用中,除了select是固定的大小因为它的底层是数组,其他两个都是动态开辟的。

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

用来操作内核事件表的,增删改:

EPOLL_CTL_ADD//添加fd上的事件
EPOLL_CTL_MOD//修改
EPOLL_CTL_DEL//删除

epfd:内核事件表文件描述符
op:指定操作类型(增删改)
fd:操作哪个文件描述符
event:指定事件,这是一个结构体(struct epoll_event)

struct epoll_event
{
    _uint32_t events;//epoll事件
    epoll_data_t data_t;//数据结构 而这又是一个联合体
}

返回值:成功返回0,失败返回-1;

typedef union epoll_data
{
    void *ptr;
    int fd; //文件描述符,但跟epoll_clt()函数的第3个参数意义不同。如果要删除一个数据就不需要这个第4个参数了。
    uint32_t u32;
    uint64_t u64;
}epoll_data_t;
3int  epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout);//内核事件表;就绪事件结构体数组;数组最大大小;超时时间

events:这个函数中的结构体数组并不是epoll_clt()函数中第4个参数event。那个是一个结构体,就是将要操作的关于一个文件描述符的事情。而这里这个events是就绪文件描述符的数组。这就是这个epoll的聪明之处,这个函数是核心函数,调用返回之后,并不像那两个把所有都返回回来(还需要循环将所有文件描述符遍历一遍看是否有文件描述符就绪),而是,只返回就绪的文件描述符,方便了很多,只要返回的就都是就绪的,只要循环处理就行了,循环的次数大大减少。
maxevents:前一个数组的最大大小。
timeout:超时时间,单位毫秒,置-1为阻塞。
返回值:就绪文件描述符个数。

原理:多了一个内核事件表(用epoll_create()函数创建),将文件描述符放入内核事件表中监听起来,内核事件表怎么标识呢?因为一切皆文件,所以我们为内核事件表专门分配一个文件描述符(一般为4,因为前三个为标准输入0输出1错误2,listenfd为3,所以为4),我们将要监听的文件描述符(用epoll_event来存放文件描述符及其事件),即将结构体event用epoll_clt()放入内核事件表中监听起来,最后用epoll_wait ()来处理内核事件表中的事件,再将表中就绪的事件通过epoll_wait()函数返回回来,这次返回回来,我们知道有n个就绪(通过返回值知道,int型),也知道是哪几个就绪(放入epoll_wait(),第二个我们传进去的参数events数组),之后直接循环n次,直接处理就行了。

讲完原理,我们还是一样,上代码:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>
#include<string.h>

#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/epoll.h>

#define MAX 1024

int 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);

    int epfd=epoll_create(1);    //创建内核事件表epfd
    assert(epfd!=-1);

    struct epoll_event ev;    
    ev.events=EPOLLIN;
    ev.data.fd=listenfd;     初始化一个关于listenfd的event结构体

    res=epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);  //将关于listenfd的结构体放入内核事件表
    assert(res!=-1);

    struct epoll_event event[MAX];    //下面epoll_wait()要将就绪事件都放入该数组中返回回来
    while(1)
    {
        int n=epoll_wait(epfd,event,MAX,-1);   //核心函数;返回就绪文件描述符个数
        if(n==-1)   
        {
            printf("error!\n");
            exit(0);
        }
        if(n==0)
        {
            printf("timeout\n");
            continue;
        }
        int i=0;
        for(;i<n;++i)
        {
            int fd=event[i].data.fd;
            if(event[i].events & EPOLLRDHUP)   //cli输入“end”
            {
                printf("break\n");
                close(fd);
                epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);   将关于fd的结构体从epfd中删除
                continue;
            }
            if(event[i].events & EPOLLIN)  
            {
                if(fd==listenfd)
                {
                    int len=sizeof(cli);
                    int c=accept(listenfd,(struct sockaddr*)&cli,&len);
                    assert(c!=-1);
                    printf("link succese\n");

                    ev.events=EPOLLIN|EPOLLRDHUP;
                    ev.data.fd=c;
                    res=epoll_ctl(epfd,EPOLL_CTL_ADD,c,&ev);
                    assert(res!=-1);

                }
                else
                {
                    char buff[128]={0};
                    int num=recv(fd,buff,127,0);
                    assert(num!=-1);
                    printf("%d:%s",fd,buff);
                    send(fd,"ok",2,0);
                }

            }
        }
    }
}

看完代码有没有很清晰呢?

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值