结构
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); }