• Linux -- Reactor


    结构

    1. handles

    资源的标志.这些资源通常包含网络连接,文件,定时器,同步对象等.handles 被用在注册服务器来标记socket,以便同步事件复用(Synchronous Event Demultiplexer)能等待这些资源就绪.注册服务器对两种事件感兴趣,一是连接事件(connetion events)一是可读事件(read events),分别代表传入新的客户端连接和记录数据.注册服务器对每个客户端都保持一个独立的连接.每一条连接对应server上的一个socket ,这个socket在reactor模式中称为handle.在这个模式中,具体的来说,handle就是concrete event handler的文件描述符

    2. synchronous events demultiplexer

    阻塞等待handles集之中有事件就绪.当有handle就绪时返回.其中一个常用的I/O事件复用就是select()函数.

    3. initiation dispatcher

    一个注册,删除,分配event handlers的接口. synchronous events demultiplexer负责等待新事件发生.当synchronous events demultiplexer检测到新事件,它将通知initiation dispatcher 去调用特定的事件处理器(event handlers).事件包括:连接接受事件(connection acceptance events),数据读写事件(data i/o事件),超时事件(time out events)

    4. event handler

    抽象类(或接口).包含一个hook method等待具体子类实现

    5. concrete event handler

    实现了hook method,也就是处理特定事件的特定方法.应用程序在initiation dispatcher中注册concrete event handler去处理特定类型的事件.当事件到达,inittiation dispatcher调用该类事件对应的concrete event handler的hook method.

    协作

    1 : 注册event handler

    应用程序在initiation dispatcher中注册concrete event handler,这就意味着应用程序把某类特定事件(handle)和此concrete event handler关联在了一起.之后若有此类事件发生,initiation dispatcher 将通知(notify)此concrete event handler.

    Initiation dispatcher 要求每个event handler回传他们自己的handle,其实就是他们自己的文件描述符fd.以便initiation dispatcher 记录.

    2 : 启动event loop

    当所有的event handlers注册完毕,应用程序调用initiation dispatcher的handle_events()函数启动event loop.event loop的具体代码见下.

    Handle_events(){
             For(;;){           //event loop
                       Select(handlers);    //select 无事件时阻塞
                       Foreach h in handlers    //遍历handlers,处理就绪的事件
                                h.handle_event(event)
                       endforeach
      }
    }

     这时,initiation dispatcher使用Synchronous Event Demultiplexer等待这些handles上的事件发生.

    3 : 事件就绪

    当一个对应着事件源的handle就绪, Synchronous Event Demultiplexer通知initiation dispatcher.例如:一个TCP socket 可读就绪

    4 : 处理事件

    收到某handle就绪的通知 后,Initiation dispatcher 触发对应event handler的hook method来处理就绪事件

    Linux中的epoll

    Epoll简直是为reactor而生的.epoll_create()创建的就是initiation dispatcher,又名reactor.

    Epoll_ctl()可以实现了注册,删除,修改handlers(或称 event).epoll_wait()就是synchronous events demultiplexer.

    标准编程:

    #define IPADDRESS   "127.0.0.1"
    #define PORT        8787
    #define MAXSIZE     1024
    #define LISTENQ     5
    #define FDSIZE      1000
    #define EPOLLEVENTS 100
    
    listenfd = socket_bind(IPADDRESS,PORT);
    
    struct epoll_event events[EPOLLEVENTS];
    
    //创建一个描述符
    epollfd = epoll_create(FDSIZE);
    
    //添加监听描述符事件
    add_event(epollfd,listenfd,EPOLLIN);
    
    //循环等待 event loop
    for ( ; ; ){
        //该函数返回已经准备好的描述符事件数目
        ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1);
        //处理接收到的连接
        handle_events(epollfd,events,ret,listenfd,buf);
    }
    
    //事件处理函数
    static void handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf)
    {
         int i;
         int fd;
         //进行遍历;这里只要遍历已经准备好的io事件。num并不是当初epoll_create时的FDSIZE。
         for (i = 0;i < num;i++)
         {
             fd = events[i].data.fd;
            //根据描述符的类型和事件类型进行处理
             if ((fd == listenfd) &&(events[i].events & EPOLLIN))
                handle_accpet(epollfd,listenfd);
             else if (events[i].events & EPOLLIN)
                do_read(epollfd,fd,buf);
             else if (events[i].events & EPOLLOUT)
                do_write(epollfd,fd,buf);
         }
    }
    
    //添加事件
    static void add_event(int epollfd,int fd,int state){
        struct epoll_event ev;
        ev.events = state;
        ev.data.fd = fd;
        epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
    }
    
    //处理接收到的连接
    static void handle_accpet(int epollfd,int listenfd){
         int clifd;     
         struct sockaddr_in cliaddr;     
         socklen_t  cliaddrlen;     
         clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen);     
         if (clifd == -1)         
         perror("accpet error:");     
         else {         
             printf("accept a new client: %s:%d
    ",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);                       //添加一个客户描述符和事件         
             add_event(epollfd,clifd,EPOLLIN);     
         } 
    }
    
    //读处理
    static void do_read(int epollfd,int fd,char *buf){
        int nread;
        nread = read(fd,buf,MAXSIZE);
        if (nread == -1)     {         
            perror("read error:");         
            close(fd); //记住close fd        
            delete_event(epollfd,fd,EPOLLIN); //删除监听 
        }
        else if (nread == 0)     {         
            fprintf(stderr,"client close.
    ");
            close(fd); //记住close fd       
            delete_event(epollfd,fd,EPOLLIN); //删除监听 
        }     
        else {         
            printf("read message is : %s",buf);        
            //修改描述符对应的事件,由读改为写         
            modify_event(epollfd,fd,EPOLLOUT);     
        } 
    }
    
    //写处理
    static void do_write(int epollfd,int fd,char *buf) {     
        int nwrite;     
        nwrite = write(fd,buf,strlen(buf));     
        if (nwrite == -1){         
            perror("write error:");        
            close(fd);   //记住close fd       
            delete_event(epollfd,fd,EPOLLOUT);  //删除监听    
        }else{
            modify_event(epollfd,fd,EPOLLIN); 
        }    
        memset(buf,0,MAXSIZE); 
    }
    
    //删除事件
    static void delete_event(int epollfd,int fd,int state) {
        struct epoll_event ev;
        ev.events = state;
        ev.data.fd = fd;
        epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
    }
    
    //修改事件
    static void modify_event(int epollfd,int fd,int state){     
        struct epoll_event ev;
        ev.events = state;
        ev.data.fd = fd;
        epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);
    }
  • 相关阅读:
    Delphi与C++的语法区别(六点区别) good
    Delphi检测用户是否具有administrator权限(OpenThreadToken,OpenProcessToken,GetTokenInformation,AllocateAndInitializeSid和EqualSid)
    QSqlDatabase::addDatabase第一次运行的时候,生成SQLite文件的同时会产生一个默认连接
    所有CN_消息的说明
    hdu 1671 Phone List(字典树)
    所有CM_消息的说明
    感悟:新事物的生命力是惊人的,存在无限的机会
    Delphi访问活动目录(使用COM,活动目录Active Directory是用于Windows Server的目录服务)
    Delphi中获取某类的祖先类及其所在单元名称(使用GetTypeData(PClass.ClassInfo)函数,并且该类是从TPersistent类的派生类才可以这么使用)
    消息函数一般是私有的,因为不需要程序员显示的调用,但子类如果需要改写这个方法,则改成保护方法Protected
  • 原文地址:https://www.cnblogs.com/tuowang/p/9399089.html
Copyright © 2020-2023  润新知