一、初始化和结束服务器端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 header
class 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;
}
};