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\n",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.\n");
        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);
}

猜你喜欢

转载自www.cnblogs.com/tuowang/p/9399089.html
今日推荐