ACE研读笔记之五-循环式日志服务器

学习ACE,不仅是它的用法,更重要的是它的代码, 而面对ACE如此庞大的代码库, 一开始会有种无从下手的感觉, 按以往的经验,从一个实例出发最容易入手.

先看C++NPv1中给出了一个网络日志服务程序 Iterative Logging Server
最简单的循环式服务器, 用原生的系统API写一个也不难, 无非是先创建socket,
再listen, accept, read/write进行与client的网络传输,但是写完以后发现有不少问题
1. 可移植性差: 不同的操作系统和环境都要重写
2. 可复用性差: 相同的代码需要在不同的应用程序中反复编写
3. 可读性差: 没有很好的划分模块, 类,函数
4. 可维护性差: 改起来挺费劲
5. 还没想出来,反正不满意,也就不贴源程序了

让我们来看看ACE是如何实现的

打开C++NPv1_Iterative_Logging_Server项目
(不得不承认,微软的IDE越做越好了,用VC2005看代码真爽,难怪Borland做不下去了)

分析一下Iterative Logging Server项目的代码, 只有包含下列文件
./examples/C++NPv1/Iterative_Logging_Server.h
I./examples/C++NPv1/terative_Logging_Server.cpp
./examples/C++NPv1/Logging_Handler.h
./examples/C++NPv1/Logging_Handler.cpp
./examples/C++NPv1/Logging_Server.h
./examples/C++NPv1/Logging_Server.cpp

仔细一看,它还包括ace目录下的若干文件
./ace/FILE_IO.h
./ace/SOCK_Acceptor.h
./ace/SOCK_Stream.h
./ace/Log_Msg.h
等等

从入口文件Iterative_Logging_Server.cpp着手,非常简单,也就几行

int  main ( int  argc,  char   * argv[])
{
  Iterative_Logging_Server server;

  
if  (server.run (argc, argv)  ==   - 1 )
    ACE_ERROR_RETURN ((LM_ERROR, 
" %p " " server.run() " ),  1 );
  
return   0 ;
}



在main函数中只是调用Iterative_Logging_Server类的Run函数

int  Logging_Server::run ( int  argc,  char   * argv[])
{
  
if  (open (argc  >   1   ?  atoi (argv[ 1 ]) :  0 ==   - 1 )
    
return   - 1 ;

  
for  (;;)  {
    
if  (wait_for_multiple_events ()  ==   - 1 )
      
return   - 1 ;
    
if  (handle_connections ()  ==   - 1 )
      
return   - 1 ;
    
if  (handle_data ()  ==   - 1 )
      
return   - 1 ;
  }


  
return   0 ;
}



Run函数可看作是一个模板函数,描述了服务器程序运行的基本步骤
1. 侦听端口
2. 等待事件
3. 处理连接
4. 处理数据

至此,基本流程了解了,再来看看具体是如何实现一个Logging server的
Iterative Logging Server项目中主要定义使用了如下几个类
class Iterative_Logging_Server
class Logging_Server
class ACE_FILE_IO
class Logging_Handler
class ACE_SOCK_Acceptor

从以上UML图中可以看出彼此间的关系

类Iterative_Logging_Server 继承自Logging_Server, 将其部分职能委托给了
类ACE_FILE_IO, Logging_Handler和ACE_SOCK_Acceptor


Iterative_Logging_Server类继承自Logging_Server类
Logging_Server是一个接口文件,后面的几类Logging server都继承自它


Logging_Server主要有run, open, wait_for_multiple_events, handle_connections,handle_data等方法,其中的 handle_connections,handle_data为纯虚方法,都是作为Hook接口要求子类实现的.
而run, open, make_log_file方法在Logging_Server类中已经给出了缺省实现

handle_connections,handle_data由类Iterative_Logging_Server 给予实现

按照run函数中给出的调用顺序一一看来.
1. open侦听端口,

int  Logging_Server::open (u_short logger_port)
{
  ACE_INET_Addr server_addr;
  
int  result;

  
if  (logger_port  !=   0 )
    result 
=  server_addr. set  (logger_port,
                              (ACE_UINT32) INADDR_ANY);
  
else
    result 
=  server_addr. set  ( " ace_logger " ,
                              (ACE_UINT32) INADDR_ANY);
  
if  (result  ==   - 1 return   - 1 ;

  
//  Start listening, enable reuse of listen address for quick restarts.
   return  acceptor_.open (server_addr,  1 );
}



Open函数中用到了两个辅助类
ACE_INET_Addr //--主要是封装了对网络地址及连接类的使用
ACE_SOCK_Acceptor //--主要是封装了对listen, accept等socket API的使用

但是在类Iterative_Logging_Server中重载了open方法

virtual   int  open (u_short port)
{
    
if  (make_log_file (log_file_)  ==   - 1 )
      ACE_ERROR_RETURN ((LM_ERROR, 
" %p " " make_log_file() " ),  - 1 );
    
return  Logging_Server::open (port);
}



也就是在调用open之前先make_log_file

int  Logging_Server::make_log_file (ACE_FILE_IO  & logging_file,
                                   ACE_SOCK_Stream 
* logging_peer)
{
  
char  filename[MAXHOSTNAMELEN  +   sizeof  ( " .log " )];

  
if  (logging_peer  !=   0 //  Use client's hostname as log file name.
    ACE_INET_Addr logging_peer_addr;
    logging_peer
-> get_remote_addr (logging_peer_addr);
    logging_peer_addr.get_host_name (filename, MAXHOSTNAMELEN);
    ACE_OS::strcat (filename, 
" .log " );
  }

  
else
    ACE_OS::strcpy (filename, 
" logging_server.log " );

  ACE_FILE_Connector connector;
  
return  connector.connect (logging_file,
                            ACE_FILE_Addr (filename),
                            
0 //  No timeout.
                            ACE_Addr::sap_any,  //  Ignored.
                             0 //  Don't try to reuse the addr.
                            O_RDWR | O_CREAT | O_APPEND,
                            ACE_DEFAULT_FILE_PERMS);
}


2. 等待事件: 在Iterative_Logging_Server中没有采用诸如select这样的多路分离技术,只有一行return 0

3. 处理连接

  virtual   int  handle_connections ()
 
{
    ACE_INET_Addr logging_peer_addr;

    
if  (acceptor ().accept (logging_handler_.peer (),
                            
& logging_peer_addr)  ==   - 1 )
      ACE_ERROR_RETURN ((LM_ERROR, 
" %p " " acceptor.accept() " ),  - 1 );

    ACE_DEBUG ((LM_DEBUG, 
" Accepted connection from %s " ,
                logging_peer_addr.get_host_name ()));
    
return   0 ;
  }



4. 处理数据

 
virtual   int  handle_data (ACE_SOCK_Stream  * )
  
{
    
while  (logging_handler_.log_record ()  !=   - 1 )
      
continue ;

    logging_handler_.close (); 
//  Close the socket handle.
     return   0 ;
  }




从对Iterative Logging Server的结构和流程分析,可以看出其中运用了常见的模式
Template, Proxy,Facade, 并对类的层次和结构做了不错的划分.
代码也写得很简炼,没闻见什么明显的Bad Smell

其中连接句柄和数据流的传递都是通过类Logging_Handler来完成的
下一回,再重点分析这个类.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值