Chinaunix首页 | 论坛 | 博客
  • 博客访问: 971033
  • 博文数量: 335
  • 博客积分: 10287
  • 博客等级: 上将
  • 技术积分: 3300
  • 用 户 组: 普通用户
  • 注册时间: 2005-08-08 15:29
文章分类

全部博文(335)

文章存档

2015年(4)

2014年(15)

2013年(17)

2012年(11)

2011年(12)

2010年(96)

2009年(27)

2008年(34)

2007年(43)

2006年(39)

2005年(37)

我的朋友

分类: C/C++

2009-02-27 16:18:09

6.1 反应器组件 ACE_Reactor

反应器的基本原理是:
 针对关心的某个事件写一个事件处理器(event_handler). 将该事件处理器登记到反应器中(同时指明关心的事件).
 然后反应器会自动检测事件的发生. 并调用预先登记的事件处理器中的回调函数.

 
所以. 用户要做的工作就是:
 创建事件处理器.
 在反应器上登记该处理器. 告诉反应器它对某个事件有兴趣
.


6.2 事件处理器

在ACE中. 反应器是ACE_Reactor类的单件对象(因为程序中通常只需要一个反应器).
反应器提供了登记/撤销 事件处理器的接口. register_handler() / remove_handler() .
这些接口要求 事件处理器必须是 ACE_Event_Handler 类型的. 所以我们的事件处理器类必须从该类继承.

在ACE_Event_Handler类中定义了一些类似 "handle_***" 的回调方法. 我们必须在派生类中重写我们敢兴趣的.
在反应器需要检测某个I/O句柄上是否有事件时. 需要知道原始句柄. 这样就需要重写事件处理器类的get_handle()函数. 
下边是ACE_Event_Handler 中声明的钩子函数:
handle_signal()  当在反应器上登记的信号发生时. 反应器回调该函数. (不懂)
handle_input()  当来自I/O设备的输入可用时. 反应器自动回调该方法.
handle_exception()  当在反应器上登记的异常事件发生时. (不懂)
handle_timeout() 当在反应器上登记的定时器超时的时候. 回调该方法.
handle_output()  当在IO设备上的输出可用时. 回调该方法.

6.2.1 登记事件处理器
使用 ACE_Reactor 类的 register_handler() 函数. 这个函数有好几个重载形式.
该函数有个参数用来指出感兴趣的事件. 它可以是下边一些常量(定义在ACE_Event_handler类中):
READ_MASK  句柄上有数据可读时 回调 handle_input()
WRITE_MASK  句柄上可写时 回调 handle_output()
TIMER_MASK  回调 handle_close()  不懂怎么用...
ACCEPT_MASK  有来自客户端的新的连接请求时 回调 handle_input()
CONNECT_MASK 建立连接时 回调 handle_input()
DONT_CALL  它用在显式拆除事件处理器的remove_handler()函数中. 表示拆除前不调用 handler_close() 函数.


6.2.2 拆除事件处理器
当不在需要处理某个事件时. 需要把对应的事件处理器从反应器中拆除. 
两种拆除事件处理器的办法:
 一种是隐式的自动拆除. 当事件处理器类中的 handle_*** 方法返回的int 小于0 时. 反应器会自动调用事件处理器
  的Handle_close()方法. 并把事件处理器拆除.
 另一种是显式拆除. 即调用 ACE_reactor::remove_handler(). 这也会调用事件处理器的handle_close(). 然后拆除.
  不过. 如果你不需要调用handle_close(). 可以给remove_handler()传递参数 ACE_Event_Handler::DONT_CALL .
  具体例子在后边会给出.


6.3 通过反应器进行事件处理
6.3.1 I/O事件多路分离

////////////////////////////////////
// 一个使用反应器的例子.
// (注意这里的例子不是71页那个. 因为那个例子小弟么看懂ing).
// 来自 <> 75页.
// 用反应器来实现一个tcp的服务器. 连接的监听. 以及在接受的新连接的上的读取事件. 都在反应器中进行.
///////////////////////////////////

//服务器端
#include "ace/Reactor.h"
#include "ace/Event_Handler.h"
#include "ace/SOCK_Acceptor.h"

const int PORT_NO = 19999; //服务器监听的端口号.
typedef ACE_SOCK_Acceptor Acceptor;
class My_Accept_Handler;

// 一个事件处理器类. 
//处理其中维护的TCP流对象 peer_ 上发生的输入事件.
class My_Input_Handler : public ACE_Event_Handler {
public:
 // 重写基类中的handle_input() 函数.
 int handle_input(ACE_HANDLE)  {  //接口中这个参数的作用是??
  peer_.recv_n(data, 12);  //书中是 peer().recv_n(data, 12);  但不知peer()从何而来....
  // 输出 data...
  return 0;  //该函数的返回值如果小于0. 则反应器会在调用完后撤销该事件处理器的登记.
 }

 // 下边的函数会被反应器使用. 来得到会发生事件的底层句柄.
 ACE_HANDLE get_handle() const {
  return peer_.get_handle();  // 取得TCP流对象的原始句柄
 }
private:
 ACE_SOCK_Stream peer_;  
 char data[12];
};

// 另一个事件处理器类. 处理接受器收到的连接的事件
class My_Accept_Handler : public ACE_Event_Handler {
public :
 My_Accept_Handler(ACE_Addr & addr) {
  open(addr);
 }
 int open(ACE_Addr &addr) {
  peer_acceptor.open(addr); // 接受器的 open() 函数 和构造函数中直接传递addr 都可以使接受器监听该地址.
  return 0;
 }
 // 重写基类中的handle_input()
 int handle_input(ACE_HANDLE handle) {
  My_Input_Handler *eh = new My_Input_Handler;  // 刚才定义的那个类.
  if (peer_acceptor.accept(   //接受一个连接
   eh->peer_,  //一个要捆绑的ACE_SOCK_Stream对象. (peer_是私有的. 这里本来不能直接访问. 偷懒一下马马虎虎..)
   0, //这个参数用来返回前来连接的客户的地址.
   0, //超时时间
   1) //这个参数不懂ing..
   == -1)
   ACE_DEBUG((LM_ERROR, "Error in connection\n"));
  //然后给反应器中增加一个事件处理器. 来处理这个新连接上的输入事件.
  ACE_Reactor::instance()->register_handler(eh, ACE_Event_Handler::READ_MASK);
  
  return -1;  //返回负数.表示希望反应器在处理一个连接请求后. 自动注销本处理器.
 }
 // 要重写基类中的get_handle() 函数. 来帮助反应器取得内部句柄
 ACE_HANDLE get_handle() const {
  return peer_acceptor.get_handle();
 }
private:
 acceptor peer_acceptor;  //接受器
};

int main() {
 ACE_INET_Addr addr(PORT_NO);
 My_Accept_Handler * eh = new My_Accept_Handler(addr);
 
 // 登记eh到反应器中
 ACE_Reactor::instance() -> register_handler(eh, ACE_Event_Handler::ACCEPT_MASK); 
 
 while(1)
  ACE_Reactor::instance() -> handle_events();  //处理事件
}

 

阅读(1013) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~