2.简介——非常简单的服务端
既然我们已经知道如何实现一个简单的客户端,那么现在我们得实现一个服务端来测试它。我们必须提供Stock和Stock_Factory这两个接口的实现,并且创建可执行的应用程序把这两个接口的实现组合在一起。
实现Stock (股票)接口
为了简单起见,让我们用固定的价格来实现Stock对象。构造函数将接收所有的参数:
class Quoter_Stock_i : public POA_Quoter::Stock {
public:
Quoter_Stock_i (const char *symbol,
const char *full_name,
CORBA::Double price);
private:
std::string symbol_;
std::string full_name_;
CORBA::Double price_;
};
在一个服务器中,用编程语言的数据和函数来实现和表示CORBA对象和函数。这些用于实现和表示CORBA对象的编程实体称为伺服代码(servants)。对象适配器(Object Adapters )把CORBA对象的世界和编程语言的伺服代码连接在一起,还提供CORBA对象的创建服务和它们的对象引用以及将请求分发到适当的伺服代码之上。
请注意基类的名称。TAO实现了CORBA 2.2版本的规范,这个规范包含了Portable Object Adapter(因此前缀为POA)。这个新的对象适配器更正了以前版本的CORBA规范的许多问题,之前使用的的对象适配器于是被称为Basic Object Adapter。不幸的事,该规范含混不清,导致了不兼容的实现。由于遵循CORBA 2.2规范,基于POA的代码几乎是完全可移植的,不可移植的地方仅仅是生成的头文件的名称和其它细微的地方。这些问题很容易通过使用帮助类中进行包装来解决,大多数情况下,生成的文件名可以通过控制IDL编译选项的方式得以处理。
一个服务端程序可能包含了多个POA实例,但所有的服务端程序至少有一个称为RootPOA的POA。
我们必须实现的操作和属性。
class Quoter_Stock_i : public POA_Quoter::Stock {
public:
// some details omitted
char *symbol ();
char *full_name ();
CORBA::Double price ();
};
// In the .cpp file:
char *
Quoter_Stock_i::symbol ()
{
return CORBA::string_dup (this->symbol_.c_str ());
}
其它属性和成员与此相似,因此我们没有在这里列出来。
对参数的内存管理规则
因为ORB将调用CORBA::string_free释放字符串,所以在返回前复制它们是很重要的。 基本的原理是通过网络,字符串必定会被某种方式进行复制,因此,客户端必须担当起释入接收到的字符串的责任。当客户端和服务端程序在同一地址空间的时候,ORB可以优化路径和直接调用服务端的操作而无须编码和解码。如果客户端同时与本地和远程服务端同时工作,它总会期待它自己的字符串。因此,服务端的实现必段分配一份拷贝并返回此拷贝,这是因为服务端对本地或远程服务端一视同仁。虽然在CORBA的世界里,对内存管理的规则比较狡猾,但这里有一些可以遵照的简单原则:
- 在返回时,你必须创建内存的一份拷贝,由调用者负责释放它。
- 相反,你必须释放返回给你的内存。被调用者创建的拷贝。
- 对于你接收到作为输入的参数,你未有管理它内存的权力,你仅有使用它的权力。如果你想在返回的时候仍要使用它,你必须为之创建一个拷贝。
完整的内存管理规则可以在Henning 和Vinoski的书中找到,也可以在CORBA规范中找到。
录入所有的代码看上去很繁琐,可否让IDL编译器帮助我们?毕意,看上去方法的声明已被作了详细的说明。答案是肯定的。TAO的IDL编译器可以生成空白的实现,而你要做的只是简单在其上作修改。生成空白的实现只需要使用-GI 选项。
$ $ACE_ROOT/TAO/TAO_IDL/tao_idl -GI Quoter.idl
空白的实现生成在QuoterI.h 和QuoterI.cpp文件中。要注意的是用了 -GI选项后在每次生成文件时总会覆盖这些文件,所以好的方法是把它拷贝成别的名称。
Stock Factory的实现
我们工厂类的第一次实现将只为两支股票提供服务,一个是“红帽公司(RHAT)”的,一个是“微软公司的(MSFT)”。
class Quoter_Stock_Factory_i : public POA_Quoter::Stock_Factory
{
public:
Quoter_Stock_Factory ();
Quoter::Stock_ptr get_stock (const char *symbol);
private:
Quoter_Stock_i rhat_;
Quoter_Stock_i msft_;
};
get_stock()方法实现很简单,仅仅是比较符号名并返回适当的对象引用。
Quoter::Stock_ptr
Quoter_Stock_Factory_i::get_stock (const char *symbol)
{
if (strcmp (symbol, "RHAT") == 0) {
return this->rhat_._this();
} else if (strcmp (symbol, "MSFT") == 0) {
return this->msft_._this ();
}
throw Quoter::Invalid_Stock_Symbol ();
}
那这里的 _this()方法是干什么的呢?在POA的映射中,通过继承,客户端的Stub和服务端的skeleton是互不关联的。你必须要么用显示的方式激活伺服(servant)(你实现的对象),要么用_this(),通过默认的OA._this()在RootPOA下面创建和注意CORBA对象,然后为新对象返回对象的引用。在后面我们还为就显式和隐式对象激活展开更多的讨论,移除关于转换对象到伺服再到对象引用或反过来都会使服务端不能正常工作,明确这这一点上是很重要的。
实现工个CORBA服务端
现在在适当的位置我们已经有了对象的实现,下一步就是要创建服务端的可执行文件。我们从ORB的初始化开始。
int main (int argc, char* argv[])
{
try {
// First initialize the ORB, that will remove some arguments...
CORBA::ORB_var orb =
CORBA::ORB_init (argc, argv,
"" /* the ORB name, it can be anything! */);
一开始,ORB开始于 POA的 {holding state},直到POA激活前,所有收到的请求都不会被处理。同时,这些请求保存于一些实现的限制中。TAO把这个限制设为0,因为在实时系统中把请求放入队列是过载和不可预测的重要来源。
对我们来说这意味着什么呢?恩,那我们必须激活POA。这个过程有点繁琐。首先我们需要得到RootROA:
CORBA::Object_var poa_object =
orb->resolve_initial_references ("RootPOA");
PortableServer::POA_var poa =
PortableServer::POA::_narrow (poa_object.in ());
resolve_initial_references()用于启动所有类型的服务,比如:Naming Service和Trading Service,同时还用于获取其它的ORB接口,比如 RootPOA,Current Oject和Policy Manager。
既然我们获得了Root POA,我们就必须取回它的POA管理器。POA管理器提供激活和休息一个或多个POA的接口。
PortableServer::POAManager_var poa_manager =
poa->the_POAManager ();
那么,我们激活POA:
poa_manager->activate ();
之后的处理过程与客户端相似,但现在要记住的是销毁POA,把所有的代码合在一起,我们得到:
int main (int argc, char* argv[])
{
try {
// First initialize the ORB, that will remove some arguments...
CORBA::ORB_var orb =
CORBA::ORB_init (argc, argv,
"" /* the ORB name, it can be anything! */);
CORBA::Object_var poa_object =
orb->resolve_initial_references ("RootPOA");
PortableServer::POA_var poa =
PortableServer::POA::_narrow (poa_object.in ());
PortableServer::POAManager_var poa_manager =
poa->the_POAManager ();
poa_manager->activate ();
// The application code goes here!
// Destroy the POA, waiting until the destruction terminates
poa->destroy (1, 1);
orb->destroy ();
}
catch (CORBA::Exception &ex) {
std::cerr << "CORBA exception raised!" << std::endl;
}
return 0;
}
现在我们创建一个股票工厂实现的实例并用_this()来激活它。
Quoter_Stock_Factory_i stock_factory_i;
Quoter::Stock_Factory_var stock_factory =
stock_factory_i._this ();
接下来把对象引用转换为IOR字符串,客户端会使用这个字符串。
CORBA::String_var ior = orb->object_to_string (stock_factory.in ());
std::cerr << ior.in () << std::endl;
这里只有一个最后的细节,我们必须运行ORB的事件循环来开始处理来自客户端的请求。
orb->run ();
从这个服务端我们拿掉了去多细节,比如如何中止事件循,如何执行伺服的内丰是,按顺序使伺服休眠,实际上这非常不灵活,但我们已经涵盖了许多其它的事情,最重要一点是我们可以测试客户端了。
练习 1
把实现丰满直民来。你无须从0开始,我们提供了这些文件:Stock_i.h, Stock_i.cpp, Stock_Factory_i.h Stock_Factory_i.cpp, Quoter.idl 以及非常有用的MPC file 。
解决方案:
你的解决方案同Server.cpp文件作比较。
测试
我们需要客户端来测试这个应用程序。我们只需要同时运行:
$ server > ior_file
$ client file://ior_file MSFT RHAT
再测试一下无效的符号!
本文介绍如何使用CORBA实现一个简单的服务端应用,包括Stock和Stock_Factory接口的具体实现过程,以及服务端程序的创建步骤。
3311

被折叠的 条评论
为什么被折叠?



