ICE的整体架构

服务器端:

服务器端通常只有一个通信器(Ice::Communicator),通信器包含了一系列的资源:

如线程池、配置属性、对象工厂、日志记录、统计对象、路由器、定位器、插件管理器、对象适配器

在通信器内,包含有一个或更多的对象适配器(Ice::ObjectAdapter),对象适配器负责提供一个或多个传输端点,并且把进入的请求分派到对应的servant中去执行。

具体实现的部分称为servant,它们为客户端发来的调用提供服务。servant向对象适配器注册以后,由对象适配器依据客户请求调用相应方法。

客户端:

客户端直接通过代理进行远程调用,就象本地调用一样简单。

通信器Ice::Communicator

通信器管理着线程池、配置属性、对象工厂、日志记录、统计对象、路由器、定位器、插件管理器、对象适配器。

通信器的几个重要方法:

std::string proxyToString (const Ice::ObjectPrx&) const;
Ice::ObjectPrx stringToProxy (const std::string&) const;


    这两个方法可以使代理对象和字符串之间互相转换。对于proxyToString方法,你也可以使用代理对象的 ice_toString方法代替(当然,你要确保是非空的代替对象)。

Ice::ObjectPrx propertyToProxy (const std::string&) const;


    这个方法根据给定名字的属性配置生成一个代理对象,如果没有对应属性,返回一个空代理。
    比如有如下属性:

MyApp.Proxy = ident:tcp -p 5000

    我们就可以这样得到它的代理对象:

Ice::ObjectPrx p = communicator->propertyToProxy("MyApp.Proxy");
 
 
Ice::Identity stringToIdentity (const std::string&) const;
std::string identityToString (const Ice::Identity&) const;


    转换字符串到一个对象标识,对象标识的定义如下:

1. namespace  Ice
2. {
3. struct  Identity
4.     {
5.         std::string name;
6.         std::string category;
7.     };
8. }



CATEGORY/NAME 。比如字符串“Factory/File

    category部分可以为空。   


Ice::ObjectAdapterPtr createObjectAdapter (const std::string&);
Ice::ObjectAdapterPtr createObjectAdapterWithEndpoints (
const std::string&, const std::string&);


    这两个方法创建新的对象适配器。createObjectAdapter从属性配置中取得端点信息,而 createObjectAdapterWithEndpoints则直接指定端点。



void shutdown ();



    关闭服务端的Ice运行时库,调用shutdown后,执行过程中的操作仍可正常完成,shutdown不会等待这些操作完成。



void waitForShutdown ();



    这个方法会挂起发出调用的线程直到通信器关闭为止。



void destroy ();



    这个方法回收通信器的相关资源,如线程、通信端点及内存资源。在离开main函数之前,必须调用destory。



bool isShutdown () const;



    如果shutdown已被调用过,则返回true。

 

初始化通信器

    在建立通信器(Ice::Communicator)期间,Ice运行时会初始化一系列的对象,这些对象一直影响通信器的整个生命周期。并且在建立通信器以后,你不能改变这些对象。所以,如果你想定制这些对象,就必须在建立通信器的过程中定义。

    在通信器建立期间,我们可以定义下面这些对象:

  • 属性表(property)
  • 日志记录器(Logger)
  • 统计对象(Stats)
  • 原生字符串与宽字符串转换器
  • 线程通知钩子

InitializationData


1. namespace  Ice {
2. struct  InitializationData {
3.         PropertiesPtr properties;
4.         LoggerPtr logger;
5.         StatsPtr stats;
6.         StringConverterPtr stringConverter;
7.         WstringConverterPtr wstringConverter;
8.         ThreadNotificationPtr threadHook;
9.     };
10. }

    这个结构中的所有成员都是智能指针类型,设置好这些成员以后,就可以通过通信器的初始化函数传入这些对象:

1. namespace  Ice {
2. int &,  char *[],
3. const  InitializationData& = InitializationData());
4.     CommunicatorPtr initialize(StringSeq&,
5. const  InitializationData& = InitializationData());
6.     CommunicatorPtr initialize(
7. const  InitializationData& = InitializationData());
8. }


    我们前面使用的Ice::Application也提供了InitializationData的传入途径:

1. namespace  Ice
2. {
3. struct  Application
4.     {
5. int  main( int ,  char *[]);
6. int  main( int ,  char *[],  const   char *);
7. int  main( int ,  char *[],  const  Ice::InitializationData&);
8. int  main( const  StringSeq&);
9. int  main( const  StringSeq&,  const   char *);
10. int  main( const  StringSeq&,  const  Ice::InitializationData&);
11.         ...
12.     };
13. }


    再回头看InitializationData结构:

properties :PropertiesPtr 类型,指定了属性表(property)对象,它就是之前《Ice属性配置 》一文中的主角。默认的属性表实现可以解析“Key = Value”这种形式的字符串(包括命令行参数和文件),如果愿意,你可以自己写一个属性表实现,用来解析xml、ini等等。

    如果要自己实现,就得完成下面这些接口(每个方法的作用请参考《Ice属性配置 》):

1. namespace  Ice
2. {
3. class  Properties :  virtual   public  Ice::LocalObject
4. {
5. public :
6. virtual  std::string getProperty( const  std::string&) = 0;
7. virtual  std::string getPropertyWithDefault( const  std::string&,
8. const  std::string&) = 0;
9. virtual  Ice::Int getPropertyAsInt( const  std::string&) = 0;
10. virtual  Ice::Int getPropertyAsIntWithDefault( const  std::string&,
11.         Ice::Int) = 0;
12. virtual  Ice::StringSeq getPropertyAsList( const  std::string&) = 0;
13. virtual  Ice::StringSeq getPropertyAsListWithDefault( const  std::string&,
14. const  Ice::StringSeq&) = 0;
15. virtual  Ice::PropertyDict getPropertiesForPrefix( const  std::string&) = 0;
16. virtual   void  setProperty( const  std::string&,  const  std::string&) = 0;
17. virtual  Ice::StringSeq getCommandLineOptions() = 0;
18. virtual  Ice::StringSeq parseCommandLineOptions( const  std::string&,
19. const  Ice::StringSeq&) = 0;
20. virtual  Ice::StringSeq parseIceCommandLineOptions( const  Ice::StringSeq&) = 0;
21. virtual   void  load( const  std::string&) = 0;
22. virtual  Ice::PropertiesPtr clone() = 0;
23. };
24. };


logger

    我们可以自己实现这个接口,以控制它的输出方向,它的定义为:

1. namespace  Ice
2. {
3. class  Logger :  virtual   public  Ice::LocalObject
4. {
5. public :
6. virtual   void  print( const  std::string& msg) = 0;
7. virtual   void  trace( const  std::string& category,
8. const  std::string& msg) = 0;
9. virtual   void  warning( const  std::string& msg) = 0;
10. virtual   void  error( const  std::string& msg) = 0;
11. };
12. }

    不用说,实现它们是一件很轻松的事情^_^,比如你可以实现这个接口把信息写到一个日志文件里,或者把它写到某个日志服务器上。

stats

1. namespace  Ice
2. {
3. class  Stats :  virtual   public  Ice::LocalObject
4. {
5. public :
6. virtual   void  bytesSent( const  std::string& protocol,
7.         Ice::Int num) = 0;
8. virtual   void  bytesReceived( const  std::string& protocol,
9.         Ice::Int num) = 0;
10. };
11. }



stringConverter :BasicStringConverter<char>类型;
    wstringConverter

UnicodeWstringConverter 、Linux/Unix 下使用的IconvStringConverter 和Windows 下使用的WindowsStringConverter

threadHook

    下面是ThreadNotification接口的定义:

1. namespace  Ice
2. {
3. class  ThreadNotification :  public  IceUtil::Shared {
4. public :
5. virtual   void  start() = 0;
6. virtual   void  stop() = 0;
7. };
8. }



    假如我们在Windows下使用了COM组件的话,就可以使用线程通知钩子在start和stop里调用 CoInitializeEx和CoUninitialize。

代码演示

修改一下Helloworld 服务器端代码,实现自定义统计对象(Stats

1. #include <ice/ice.h>
2. #include "printer.h"
3.   
4. using   namespace  std;
5. using   namespace  Demo;
6.   
7. struct  PrinterImp : Printer{
8. virtual   void  printString( const  ::std::string& s,  const  ::Ice::Current&)
9.     {
10.         cout << s << endl;    
11.     }
12. };
13.   
14. class  MyStats :  public  Ice::Stats {
15. public :
16. virtual   void  bytesSent( const  string &prot, Ice::Int num)
17.     {
18. ": sent "  << num <<  "bytes"  << endl;
19.     }
20. virtual   void  bytesReceived( const  string &prot, Ice::Int num)
21.     {
22. ": received "  << num <<  "bytes"  << endl;
23.     }
24. };
25.   
26. class  MyApp :  public  Ice::Application{
27. public :
28. virtual   int  run( int  n,  char * v[]){
29.         Ice::CommunicatorPtr& ic = communicator();
30.         ic->getProperties()->parseCommandLineOptions(
31. "SimplePrinterAdapter" , Ice::argsToStringSeq(n,v));
32.         Ice::ObjectAdapterPtr adapter
33. "SimplePrinterAdapter" );
34. new  PrinterImp;
35. "SimplePrinter" ));
36.   
37.         adapter->activate();
38.         ic->waitForShutdown();
39. return  0;
40.     }
41. };
42.   
43. int  main( int  argc,  char * argv[])
44. {
45.     MyApp app;
46.     Ice::InitializationData id;
47. new  MyStats;
48.   
49. return  app.main(argc, argv, id);
50. }


编译运行这个演示代码,然后执行客户端,可以看到打印出的接收到发送字符数。

tcp: send 14bytes
tcp: received 14bytes
tcp: received 52bytes
tcp: send 26bytes
tcp: received 14bytes
tcp: received 53bytes
Hello World!
tcp: send 25bytes
tcp: received 14bytes



对象代理(Object Proxy)

    在客户端,我们使用对象代理进行远程调用,就如它们就在本地一样。但有时,网络问题还是要考虑的,于是Ice的对象代理提供了几个包装方法,以支持一些网络特性:

ice_timeout方法 ,声明为:Ice::ObjectPrx ice_timeout(int) const;

示例代码

1. Filesystem::FilePrx myFile = ...;
2. FileSystem::FilePrx timeoutFile
3. = FileSystem::FilePrx::uncheckedCast(
4.         myFile->ice_timeout(5000));
5. try  {
6. // Read with timeout
7. } catch ( const  Ice::TimeoutException &) {
8. "invocation timed out"  << endl;
9. }
10. Lines text = myFile->read(); // Read without timeout


ice_oneway方法 ,声明为:Ice::ObjectPrx ice_oneway() const;

示例代码

1. Ice::ObjectPrxo=communicator->stringToProxy( /* ... */ );
2. // Get a oneway proxy.
3. Ice::ObjectPrx oneway = o->ice_oneway();
4.   
5. // Down-cast to actual type.
6. PersonPrx onewayPerson = PersonPrx::uncheckedCast(oneway);
7. // Invoke an operation as oneway.
8. try  {
9.     onewayPerson->someOp();
10. } catch  ( const  Ice::TwowayOnlyException &) {
11. "someOp() is not oneway"  << endl;
12. }


ice_datagram方法 ,声明为:Ice::ObjectPrx ice_datagram() const;

示例代码

1. Ice::ObjectPrxo=communicator->stringToProxy( /* ... */ );
2. // Get a datagram proxy.
3. //
4. Ice::ObjectPrx datagram;
5. try  {
6.     datagram = o->ice_datagram();
7. } catch  ( const  Ice::NoEndPointException &) {
8. "No endpoint for datagram invocations"  << endl;
9. }
10. // Down-cast to actual type.
11. //
12. PersonPrx datagramPerson = PersonPrx::uncheckedCast(datagram);
13. // Invoke an operation as a datagram.
14. //
15. try  {
16.     datagramPerson->someOp();
17. } catch  ( const  Ice::TwowayOnlyException &) {
18. "someOp() is not oneway"  << endl;
19. }



批量调用代理 :
Ice::ObjectPrx ice_batchOneway() const; Ice::ObjectPrx ice_batchDatagram() const; void ice_flushBatchRequests();

为了提供网络效率,对于单向调用,可以考虑把多个调用打包一起送往服务器,Ice对象代理提供了ice_batchOneway 和ice_batchDatagram

示例代码

1. Ice::ObjectPrx base = ic->stringToProxy(s);
2. PrinterPrx printer =  PrinterPrx::uncheckedCast(base->ice_batchOneway());
3. if (!printer)  throw   "Invalid Proxy!" ;
4. printer->printString("Hello" );
5. printer->printString("World" );
6. printer->ice_flushBatchRequests();