频道栏目
读书频道 > 其他 > ACE技术内幕:深入解析ACE架构设计与实现原理
2.3.1 I/O事件处理器的实现
2012-08-16 20:39:08     我来说两句
收藏   我要投稿
本书从构架模式、编程示例和源代码3个维度系统地对经典网络框架ACE(Adaptive Communication Environment)的架构设计和实现原理进行了深入分析,它能解决4个方面的问题:第一,帮助框架设计者领略软件框架设...  立即去当当网订购
我们先来展示用于处理客户端数据的I/O事件处理器—Handle_data类,如代码清单2-1所示。在分析之前,读者可以先浏览一下ACE_Event_Handler接口的源代码,它是一个接口,函数的实现非常简单。对ACE_Event_Handler接口有一个初步的认识,有助于我们理解Acceptor类和Handle_data类的设计。 
Handle_data类是ACE_Event_Handler的子类,它是一个具体事件处理器,每一个客户端连接都有一个Handle_data对象与之对应。Handle_data类的主要工作是处理连接上的I/O数据,回忆第1章的分析,可以发现,这个任务可以交给ACE_SOCK_Stream类来完成,所以Handle_data类包含了一个ACE_SOCK_Stream类的数据成员,一旦连接成功,就可以将Socket描述符保存在该数据成员中,并由它真正地完成I/O操作。读者可能要问:我们已经有了发送和接收Socket数据的ACE_SOCK_Stream类,为什么还要设计Handle_data类?在第1章我们已经分析了,ACE_SOCK_Stream类只是对Socket I/O的一个简单封装,并不能直接应用于Reactor框架,在调度事件过程中,Reactor框架只能处理具体的事件处理器对象,根本无法处理其他的对象。 
 
代码清单2-1   Handle_data类 
------------------------------------------------- handle_data.h 
// Handle_data类是ACE_Event_Handler接口的子类 
class Handle_data: public ACE_Event_Handler 

public:      
/* Handle_data的构造函数。事件处理器对象有一个指针指向Reactor管理器,默认使用Reactor管理 
器的instance函数返回的Singleton对象*/ 
      Handle_data( ACE_Reactor *r = 
ACE_Reactor::instance() ):ACE_Event_Handler(r){} 
      // open函数是Handle_data类的初始化函数 
      ACE_INT32 open( ); 
      /* handle_xxx函数是事件处理器接口的模板函数,具体的事件处理器可以根据对事件的处理有选择地实现*/ 
      ACE_INT32 handle_input( ACE_HANDLE = ACE_INVALID_HANDLE ); 
      ACE_INT32 handle_close( ACE_HANDLE=ACE_INVALID_HANDLE, ACE_Reactor_Mask mask=0); 
      /* 每一个具体的I/O事件处理器都和一个描述符对应,get_handle函数被框架调用,用于返回事 
      件处理器的描述符,该描述符用于框架分离和调度事件*/ 
      ACE_HANDLE get_handle( ) const 
       { 
        return peer_.get_handle(); 
       } 
      ACE_SOCK_Stream &get_peer() 
       { 
          return peer_; 
       } 
private:     
   ~Handle_data(){ACE_DEBUG( (LM_DEBUG, "handle data ~dctor .\n") );}; 
private:     
//ACE_SOCK_Stream数据成员表示一个流,用于保存Socket描述符和对Socket进行I/O操作 
   ACE_SOCK_Stream  peer_; 
}; 
------------------------------------------------- handle_data.h 
Handle_data类中的函数可以简单地分为两类:一类继承自ACE_Event_Handler接口,另一类是新定义的函数。这两类函数对框架而言是不同的。继承自ACE_Event_Handler接口的函数对框架而言是可见的,框架会在某一时刻调用它们,而新定义的函数对框架而言是不可见的,它由应用程序自行调用。例如,在代码清单2-1中,open函数是一个新定义的函数,它必须由应用程序自己调用,以完成初始化工作。get_handle函数继承自ACE_Event_Handler接口,框架调用该函数来获取与事件处理器对应的事件描述符。这样的分类同样适用于其他框架。 
细心的读者会发现,Handle_data类的析构函数的访问属性被设计为私有的,这样的设计并非偶然。对于开发者而言,框架的自动化能力(比如自动创建和释放具体的事件处理器对象)越高,应用程序开发就越简单。对于Reactor框架,如果每一个具体的事件处理器对象都可以在框架中自动地创建和释放,将会大大减少应用程序对事件处理器的管理难度。如果析构函数被设计为私有的,那么Handle_data对象就只能通过new函数来创建,只能通过delete函数来释放,这样可以避免一些错误使用。Handle_data对象是如何释放的,我们将放在handle_close函数中具体分析。 
在分析了事件处理器类的实现之后,我们来分析事件处理器函数的实现。 
1. open函数 
open函数是Handle_data类的初始化函数,但是它不是事件处理器接口的模板函数,所以应用程序必须自己对它调用,框架根本看不到事件处理器抽象接口以外的函数,open函数的定义如代码清单2-2所示。在本示例中,调用open函数表示连接建立已经成功,新连接的Socket描述符已经被保存在peer_数据成员中,具体的调用场景读者可以参考Acceptor类的handle_input函数。 
代码清单2-2   Handle_data类的open函数 
----------------------------------------------- handle_data.cpp 
ACE_INT32 Handle_data::open( ) 

    ACE_INT32 ret = 0; 
    ACE_INET_Addr remote_addr; 
    //获取客户端的地址,保存在remote_addr中 
    get_peer().get_remote_addr( remote_addr );       
    ACE_DEBUG( (LM_DEBUG, "the remote addr is %s\n", 
    remote_addr.get_host_addr())  ); 
 
    /*调用register_handler函数将事件处理器注册到Reactor框架中,注册的事件类型为 
    ACE_Event_Handler::READ_MASK,表示只处理读事件*/ 
    ret = reactor()->register_handler( this, 
    ACE_Event_Handler::READ_MASK ); 
    if (ret != -1)   
    {        
        ACE_DEBUG( (LM_DEBUG, "handle data register ok!\n")  );      
    } 
        return ret; 

----------------------------------------------- handle_data.cpp 
不同的事件处理器,其初始化过程互不相同。但是,在初始化过程中必须做一件共同的事情,那就是将事件处理器注册到Reactor框架中,让其接受框架的调度。对于同一个描述符,可以有多种不同的事件,如READ、WRITE。对于Socket描述符还有CONNECT、ACCEPT事件。在注册一个事件处理器时,必须告诉框架,该处理器需要处理什么事件。示例的事件处理器只处理READ事件,所以将register_handler函数的第二个参数设置为ACE_Event_Handler::READ_MASK。 
提示   事件处理器只有注册到框架中才能接受框架的调度。 
2. handle_input函数 
当同步事件分离函数在Socket描述符上收到一个READ事件后,Reactor框架将调度该事件,由事件处理器的handle_input函数处理该事件。我们的handle_input函数实现非常简单,如代码清单2-3所示,它只读取数据,然后将数据通过调试接口输出。 
代码清单2-3   I/O事件处理器的handle_input函数 
----------------------------------------------- handle_data.cpp 
ACE_INT32 Handle_data::handle_input( ACE_HANDLE handle) 
/*参数handle表示与调度事件相对应的描述符,框架会将描述符传递给应用程序的处理函数,至于是否要 
使用,完全取决于应用程序*/ 
{     
 ACE_INT8 buf[512] = {0}; 
 ACE_INT32 len; 
  
 len = get_peer().recv( buf, 500); 
 //如果接收的数据长度大于0,那么将数据输出,然后返回0 
 if (len > 0) 
 { 
   ACE_DEBUG( (LM_DEBUG, "recv data %s, len:%d.\n", buf, len) ); 
   return 0; 
 } 
 //如果接收的数据长度等于0,那么表示对端关闭了Socket,返回-1 
 else if (len == 0) 
 { 
   ACE_DEBUG( (LM_DEBUG, "recv data len is 0, client exit.\n") ); 
   return -1; 
 } 
 //如果调用发生错误,则返回-1 
 else 
 { 
   ACE_DEBUG( (LM_DEBUG, "recv data error len < 0" ) ); 
   return -1; 
 } 

----------------------------------------------- handle_data.cpp 
handle_input函数是事件处理器的模板函数,会被框架调用。在很多情况下,handle_input函数的返回值对框架是有意义的,框架会根据返回值做一些处理。如果handle_input函数返回0,那么框架将会继续调度该事件处理器;如果返回-1,那么框架会关闭该事件处理器。在代码清单2-3中,在正确接收到数据的情况下返回0,框架继续调度该事件处理器;在对方关闭Socket或发生错误的情况下返回-1,框架关闭该事件处理器。在分析Reactor管理器时,我们会对此进行更深入的分析。 
3. handle_close函数 
handle_close函数是一个特殊的函数,如代码清单2-4所示,当事件处理器对某个事件进行处理后返回小于0的值时,Reactor框架会自动调用该函数来执行关闭操作。 
代码清单2-4   I/O事件处理器的handle_close函数 
----------------------------------------------- handle_data.cpp 
ACE_INT32 Handle_data::handle_close( ACE_HANDLE ,ACE_Reactor_Mask ) 
{     
  get_peer().close(); //关闭Socket 
  ACE_DEBUG( (LM_DEBUG, "handle data close.\n") ); 
  delete this;         //释放事件处理器对象 
  return 0; 

----------------------------------------------- handle_data.cpp 
handle_close函数是事件处理器接口的模板函数,应用程序可以通过该函数实现一些清理工作,比如关闭文件,释放内存等。返回到代码清单2-3中,如果handle_input函数返回-1,表示对端关闭了Socket或接收数据时发生了错误。无论是哪一种情况,都应该进行一些清理操作,并且最好由框架自动调用,此时,Reactor框架调用handle_close函数,框架执行这样的操作是符合实际的Socket编程规范的。 
在handle_close函数中,我们还调用了delete函数来释放事件处理器对象。在代码清单2-1中,我们将Handle_data类的析构函数设计为私有的,这样可保证Handle_data对象只能通过handle_close函数来释放。对内存管理越严格,内存问题出现的概率也就越低。 
提示   ACE把handle_close执行的关闭方式称为隐式关闭,与之相对应的是使用remove_handler函数进行的关闭操作,称为显式关闭。隐式关闭多用于框架内部,而显式关闭多用于应用程序。
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:2.3 Reactor框架应用示例
下一篇:2.3.2 Accept事件处理器的实现
相关文章
图文推荐
排行
热门
最新书评
特别推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站