ACE反应器(Reactor)模式(2)
转载于:http://www.cnblogs.com/TianFang/archive/2006/12/18/595808.html
在Socket编程中,常见的事件就是"读就绪","写就绪",通过对这两个事件的捕获分发,可以实现Socket中的异步操作。
Socket编程中的事件处理器
在前面我们已经介绍过,在ACE反应器框架中,任何都必须派生自ACE_Event_Handler类,并通过重载其相应会调事件处理函数来实现相应的回调处理的。在Socket编程中,我们通常需要重载的函数有
- handle_input()
当I/O句柄(比如UNIX中的文件描述符)上的输入可用时,反应器自动回调该方法。 - handle_output()
当I/O设备的输出队列有可用空间时,反应器自动回调该方法。 - handle_close()
当事件处理器中的事件从Reactor中移除的时候调用。
此外,为了使Reactor能通过I/O句柄找到对应的事件处理器,还必须重载其get_handle()方法以使得Reactor建立起I/O句柄和事件处理器的关联。
使用Reactor框架。
下面我们将以一个客户端的程序为例,介绍如何在Socket编程中使用Reactor框架。
一.建立一个客户端对象(事件处理器)。
客户端对象就是一个事件处理器,其声明如下:
1 class Client:public ACE_Event_Handler 2 { 3 public: 4 ACE_HANDLE get_handle(void) const; 5 int handle_input (ACE_HANDLE fd); 6 int handle_close (ACE_HANDLE handle, 7 ACE_Reactor_Mask close_mask); 8 ACE_SOCK_Stream& Peer(); 9 private: 10 ACE_SOCK_Stream peer; 11 };
在Client端中我只关心"读就绪"事件,故只重载了handle_input函数(大多数应用下只需要重载handle_input函数)。另外,在客户端还保存了一个ACE_SOCK_Stream的peer对象用来进行Socket通信,同时封装了一个Peer()函数返回它的引用。
二.重载相应回调处理函数
1 ACE_SOCK_Stream& Client::Peer() 2 { 3 return peer; 4 } 5 6 ACE_HANDLE Client::get_handle(void) const 7 { 8 return peer.get_handle(); 9 } 10 11 int Client::handle_input (ACE_HANDLE fd) 12 { 13 int rev=0; 14 if((rev = peer.recv(buffer,1000))>0) 15 { 16 buffer[rev]='\0'; 17 cout<<endl<<"rev:\t"<<buffer<<endl; 18 return 0; 19 } 20 else //Socket连接发生错误,返回-1,在Reactor中注销事件,触发handle_close函数 21 { 22 return -1; 23 } 24 } 25 26 int Client::handle_close (ACE_HANDLE handle, 27 ACE_Reactor_Mask close_mask) 28 { 29 cout<<endl<<"connecetd closed"; 30 return ACE_Event_Handler::handle_close(handle,close_mask); 31 }
几个函数的功能都非常简单,这里就不多做介绍了。
三.在Reactor中注册事件
首先让我们来看看相应的main函数的代码:
1 int main(int argc, char *argv[]) 2 { 3 Client client; 4 ACE_SOCK_Connector connector; 5 ACE_INET_Addr addr(3000,"127.0.0.1"); 6 ACE_Time_Value timeout(5,0); 7 if(connector.connect(client.Peer(),addr,&timeout) != 0) 8 { 9 cout<<endl<<"connecetd fail"; 10 return 0; 11 } 12 13 ACE_Reactor::instance()->register_handler(&client,ACE_Event_Handler::READ_MASK); 14 15 while(true) 16 { 17 ACE_Reactor::instance()->handle_events(); 18 } 19 20 return 0; 21 }
在这里可以看到,使用Reactor框架后,依然首先通过ACE_SOCK_Connector的connect函数来建立连接。建立连接后,可以通过ACE_Reactor::instance()->register_handler函数来实现Reactor的注册,实现I/O事件和Client对象的handle_input方法相关联,它的第一个参数是事件处理器的地址,第二个参数是事件类型,由于这里只关心读就绪事件,故注册的事件类型是ACE_Event_Handler::READ_MASK。
四.启动Reactor事件循环
通过如上设置后,我们就可以通过ACE_Reactor::instance()->handle_events()启动Reactor循环了,这样,每当服务器端有数据发送给客户端时,当客户端的数据就绪时,就回触发Client对象的handle_input函数,将接收的数据打印出来。
通常的做法是,将Reactor事件循环作为一个单独的线程来处理,这样就不会阻塞main函数。
五.注销Reactor事件
Reactor事件的注销一般有两种方式,显式和隐式,下面将分别给予介绍。
- 隐式注销。
当Reactor捕获事件后,会触发相应的"handle_"处理函数,当"handle_"处理函数返回值大于或等于0时,表示处理事件成功,当返回值小于0时,表示处理事件失败,这时Reactor会自动注销该句柄所注册的所有事件,并触发handle_close函数,以执行相应的资源清理工作。
在本例中,当handle_input函数里的recv函数接收到0个数时,说明socket发生错误(大多为Socket连接中断),此时返回-1,则系统自动注销相应事件。 - 显示注销。
调用Reactor对象的remove_handler方法移除,它有两个参数,第一个是所注册的事件反应器对象,第二个是所要注销的事件。
在这个示例程序里,连接方只有一个Socket连接,Reactor的优势并没有体现出来,但在一些网络管理系统里,连接方需要对多个需要管理的设备(服务器端)进行连接,在这种情况下使用Reactor模式,只需要多开一个Reactor事件循环线程就能实现事件多路分发复用,并且不会阻塞,通过面向对象的回调方式管理,使用起来非常方便。
Reactor框架的另外一个常用的地方就是服务器端,一般是一个服务器端对应多个客户端,这样用Reactor模式能大幅提高并发能力,这方面的编程方法将在下一章给与介绍。