Ice笔记--服务端的Slice-to-C++

来源:互联网 发布:淘宝店招导航全屏代码 编辑:程序博客网 时间:2024/05/21 22:50

一、初始化和结束服务器端Ice run time

     初始化和结束服务器端Ice run time的实现可分为三种方式:常用main函数实现,利用Ice::Application类实现,利用Ice::Service类等。

   1)服务端main函数

        1.1)初始化Ice run time

          Ice run time 的主要进入点是由本地接口Ice::Communicator表示,在程序的开始必须首先调用Ice::Initialize对Ice run time 进行初始化;Ice::Communicator返回一个智能指针,指向一个   Ice::Communicator实例。

          初始化的代码在服务端main函数实现大致如:Ice::CommunicatorPtr  ic = Ice::initialize(argc , argv); 。如果在初始化过程中出了任何问题,Ice::initialize 会抛出异常。

        1.2)结束Ice run time

          在离开main函数之前,必须调用Communicator::destroy来结束Ice run time 。 destroy会等待任何正在运行的操作调用完成;如果在调用destroy函数之前就终止main函数,会导致不确定的后果。

        1.3)服务器main函数类似实现如下:

#include <Ice/Ice.h>int main(int argc, char * argv[]){    int status = 0;    Ice::CommunicatorPtr ic;    //这段代码把对Ice::initialize 的调用放在了try 块中,并且会负责把正确的退出状态返回给操作系统    try {        ic = Ice::initialize(argc, argv);        // Server code here...    } catch (const Ice::Exception & e) {        cerr << e << endl;        status = 1;    } catch (const std::string & msg) {        cerr << msg << endl;        status = 1;    } catch (const char * msg) {        cerr << msg << endl;        status = 1;    }    if (ic)    {        ic->destroy();    }    return status;}

   2)Ice::application类

        由于上述的main函数结构很常用,所以有必要将这些操作封装起来。 Ice::application类则封装了所有正确的初始化和结束活动。

           2.1)application类定义

namespace Ice{   class Application   {    public:       Application();       virtual ~Application();       int main(int, char * [], const char * = 0);       virtual int run(int, char * []) = 0;       static const char * appName();       static CommunicatorPtr communicator();       // ...    };}

        2.2)实现并使用继承自Ice::application的子类

          Ice::Application 能够确保你的程序适当地结束Ice run time,不管服务器是正常终止,还是因为对异常或信号作出响应而终止的。因此建议在所有应用程序中使用这个类。

         其程序代码的实现大致如下:          

#include <Ice/Ice.h> class MyApplication : virtual public Ice::Application {public:    virtual int run(int, char * [])    {    // Server code here...    return 0;    }}; int main(int argc, char * argv[]){    MyApplication app;    return app.main(argc, argv);}

       2.3)使用Ice::Service类

          相对于Ice::application类来说,建议使用Ice::Service的情况会比较少。当应用可能需要作为Unix 看守(daemon)或Win32 服务运行在系统一级时,Ice::Service可以提供相关功能实现。

          这里不再详细介绍Ice::Service。详细资料可以参考 《Ice-1.3.0_cn.pdf》第10章中的内容。

二、接口的映射

   1)骨架类

    在客户端,接口映射到代理类(参见5.12 节)。在服务器端,接口映射到骨架类。对于相应的接口上的每个操作,骨架类都有一个对应的纯虚方法。

    例如,有一个Node接口的定义:

module Filesystem{    interface Node    {        nonmutating string name();    };    // ...};

 Slice 编译器为这个接口生成这样的定义:

namespace Filesystem{    class Node : virtual public   Ice::Object    {    public:    virtual std::string name(const Ice::Current & = Ice::Current()) const = 0;    // ...    };    // ...}

   下面是骨架类的几点特性:

    • 和客户端一样,Slice 模块映射到名字相同的C++ 名字空间,所以骨架类定义会放在名字空间Filesystem 中。

    • 骨架类的名字与Slice 接口的名字(Node)相同。

    • 对于Slice 接口中的每个操作,骨架类都有一个对应的纯虚成员函数。

    • 骨架类是抽象基类,因为它的成员函数是纯虚函数。

    • 骨架类继承自Ice::Object (这个类形成了Ice 对象层次的根)。

   2)Servant类

   要给Ice 对象提供实现,我们必须创建servant 类,继承对应的骨架类。例

   如,要为Node 接口创建servant,可以编写:

#include <Filesystem.h> // Slice-generated headerclass NodeI : public virtual Filesystem::Node{public:    NodeI(const std::string &);    virtual std::string name(const Ice::Current &) const;private:    std::string _name;};

       2.1)实例化servant

         首先在堆上创建一个显得NodeI实例,把它的地址赋值给类型为NodeIPtr的智能指针:

typedef IceUtil::Handle<NodeI> NodeIPtr; NodeIPtr servant = new NodeI("Fred"); 

        使用智能指针的好处是避免偶然发生内存泄露。

      2.2)创建标识

        每一个Ice对象都需要一个标识。在使用同一个对象适配器的所有servant中,该标识必须是唯一的。Ice 对象标识是一种结构,下面是它的Slice 定义: 

module Ice{  struct Identity   {//对象的完整标识由Identity 的name 和category 域组成       string name;    string category;  };   // ...}; 

     2.3)激活servant

        只有在你显式地把servant 告知对象适配器之后, Ice run time 才会知道这个servant 的存在。要激活servant,就要调用对象适配器的add 操作:

void activateServant(const string & name){  NodePtr servant = new NodeI(name); // Refcount == 1  Ice::Identity id;   id.name = name;   _adapter->add(servant, id); // Refcount == 2} // Refcount == 1

调用对象适配器的add 操作,会把servant 指针和servant 的标识增加到适配器的servant 映射表中,并把“Ice 对象的代理”与“服务器内存中的正确的servant 实例”链接在一起。

        同时Ice也提供了addwithUUID()函数,只要一步,就可以生成一个UUID、并把servant 增加到servant 映射表中。这样就避免了额外创建标识的操作。重写上面的函数

void activateServant(const string & name){  NodePtr servant = new NodeI(name); _adapter->addWithUUID(servant);}

   2.4)创建代理

       一旦我们激活了Ice 对象的servant,服务器就可以处理针对这个对象的客户请求了。但是,只有拥有了对象的代理,客户才能访问该对象。

       而象适配器含有创建代理所需的全部详细资料:寻址信息和协议信息,还有对象标识。对象适配器的add 和addWithUUID servant 激活操作会返回对应的Ice 对

       象的一个代理。这意味着,我们可以编写:

typedef IceUtil::Handle<NodeI>NodeIPtr;NodeIPtr servant = new NodeI(name);NodePrx proxy = NodePrx::uncheckedCast(_adapter->addWithUUID(servant));// Pass proxy to client...

        在此我们需要使用uncheckedCast,因为addWithUUID 返回的代理的类型是Ice::ObjectPrx

三、传递参数和抛出异常

   1)参数传递

      服务器端的参数传递所遵循的规则和客户端一样:

          • in 参数通过值或const 引用传递。

          • out 参数通过引用传递。

          • 返回值通过值传递。

      例子说明:

interface Example{    string op(string sin, out string sout);};
为这个接口生成的骨架类:
class Example : virtual public ::Ice::Object{public:    virtual std::stringop(const std::string &, std::string &,    const Ice::Current & = Ice::Current()) = 0;    // ...};

    2)引发异常

        要从操作实现中抛出异常,你只需实例化异常,初始化,然后抛出它。例如:

void Filesystem::FileI::write(const Filesystem::Lines & text,const Ice::Current &){   // Try to write the file contents here...   // Assume we are out of space...  if (error) {      Filesystem::GenericError e;    e.reason = "file too large";    throw e;   }};
原创粉丝点击