【 colorado 】
本文介绍了比较完整的Ice应用程序,它实现了简单文件系统。本程序位于ICE发布的$ICE_HOME/demo/book/simple_filesystem目录。本文内容涉及DPWI第5,7,9章。通过这个程序的学习,读者应该掌握了Ice应用程序开发的基本方法、步骤。此后要通过深入学习Ice知识来提高Ice应用程序的开发水平。到本文为止,Ice for C++ 应用程序开发的基础部分就介绍完了。
1、系统需求
该系统为层次结构,由目录和文件组成。目录是子目录、文件的容器,顶层为根目录。同级各个目录、文件不能同名。
该系统包含固定数目的目录、文件。不能创建、销毁目录、文件。只能一次读写文件全部内容,不能读写部分内容,不支持路径。
2、定义Slice文件
Filesystem.ice:
module Filesystem {
interface Node {
idempotent string name();
};
exception GenericError {
string reason;
};
sequence<string> Lines;
interface File extends Node {
idempotent Lines read();
idempotent void write(Lines text) throws GenericError;
};
sequence<Node*> NodeSeq;
interface Directory extends Node {
idempotent NodeSeq list();
};
};
根据规范:
• Node为节点,提供name操作;文件File和目录Direcotry继承自Node。
• File只支持文本文件,read不会失败,只有write会遇到异常。read/write是幂等操作。
• Directory提供了列出内容的list操作。返回结果为Node序列,序列中包含的是Node代理,这里File/Directory的基代理,可以转型为相应的具体代理。
3、服务器端
服务器由以下文件组成:
• Server.cpp :这个文件含有服务器主程序。
• FilesystemI.h:这个文件含有文件系统服务者的定义。
• FilesystemI.cpp:这个文件含有文件系统服务者的实现。
3.1、主程序
Server.cpp main函数:
int main(int argc, char* argv[])
{
FilesystemApp app;
return app.main(argc, argv);
}
定义了FilesystemApp作为启动类,该类继承自Ice::Application类,在run成员函数中实现服务器
主流程:
virtual int run(int, char*[]) {
// 收到中止信号时干净地终止程序
shutdownOnInterrupt();
// 创建对象适配器
Ice::ObjectAdapterPtr adapter =
communicator()->createObjectAdapterWithEndpoints("SimpleFilesystem", "default -p 10000");
// 创建根目录(名为"/",没有父节点)
DirectoryIPtr root = new DirectoryI(communicator(), "/", 0);
root->activate(adapter);
// 在根目录下创建README文件
FileIPtr file = new FileI(communicator(), "README", root);
Lines text;
text.push_back("This file system contains a collection of poetry.");
file->write(text);
file->activate(adapter);
// 在根目录下创建Coleridge目录
DirectoryIPtr coleridge = new DirectoryI(communicator(), "Coleridge", root);
coleridge->activate(adapter);
// 在Coleridge目录下创建Kubla_Khan文件
file = new FileI(communicator(), "Kubla_Khan", coleridge);
text.erase(text.begin(), text.end());
text.push_back("In Xanadu did Kubla Khan");
text.push_back("A stately pleasure-dome decree:");
text.push_back("Where Alph, the sacred river, ran");
text.push_back("Through caverns measureless to man");
text.push_back("Down to a sunless sea.");
file->write(text);
file->activate(adapter);
// 所有对象已创建,现在可以接收客户端请求
adapter->activate();
// 等待完成
communicator()->waitForShutdown();
if (interrupted()) {
cerr << appName()
<< ": received signal, shutting down" << endl;
}
return 0;
};
⑴、安装shutdownOnInterrupt(),遇到关闭信号,就关闭程序。
⑵、创建对象适配器adapter,适配器名SimpleFilesystem,协议:缺省(TCP),端口:10000。
⑶、创建文件系统
文件系统结构:
RootDir
↙ ↘
Coleridge README
↓
Kubla_Khan
• 首先调用new DirectoryI(communicator(), "/", 0) 创建根目录"/",根没有父目录,传入0作为父目录句柄。返回目录指针存入root;
• 然后调用new FileI(communicator(), "README", root)创建文件README,它的父目录为root;为文件添加内容;
• 继续调用new DirectoryI(communicator(), "Coleridge", root)创建子目录Coleridge;
• 最后调用new FileI(communicator(), "Kubla_Khan", coleridge)创建文件Kubla_Khan,它的父目录为Coleridge;为文件添加内容;
每次创建节点后,都要调用NodeI::activate(...),将该节点添加到适配器adapter中,也将节点加入它的父目录。
⑷、激活适配器
调用adapter->activate()激活适配器;
⑸、关闭程序
调用communicator()->waitForShutdown()
挂起当前主函数所在线程,等待通讯器关闭;如果通讯器关闭了,就检查是否正常关闭,如果是由于关闭信号(如Ctrl+C)引发的关闭,打印关闭信号信息;关闭程序。
3.2 服务者类定义
slice2cpp编译Filesystem.ice生成Filesystem.h,Filesystem.cpp映射文件,定义了文件系统接口规范。FilesystemI.h,FilesystemI.cpp继承了映射文件中的类并予以实现。为了避免错误,加快生成,可以使用如下命令:
slice2cpp --impl Filesystem.ice
这会同时生成如下文件:
Filesystem.h
Filesystem.cpp
FilesystemI.h
FilesystemI.cpp
这样,开发FilesystemI就不容易出错了。
FilesystemI.h:
namespace Filesystem {
class DirectoryI; //提前声明,DirectoryIPtr引用之
typedef IceUtil::Handle<DirectoryI> DirectoryIPtr; //提前声明,NodeI,FileI引用之
class NodeI : virtual public Node {
public:
virtual std::string name(const Ice::Current&);
NodeI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&);
void activate(const Ice::ObjectAdapterPtr&);
private:
std::string _name;
Ice::Identity _id;
DirectoryIPtr _parent;
NodeI(const NodeI&); // 禁止复制
void operator=(const NodeI&); // 禁止赋值
};
typedef IceUtil::Handle<NodeI> NodeIPtr;
class FileI : virtual public File, virtual public NodeI {
public:
virtual Lines read(const Ice::Current&);
virtual void write(const Lines&,const Ice::Current& = Ice::Current());
FileI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&);
private:
Lines _lines;
};
typedef IceUtil::Handle<FileI> FileIPtr;
class DirectoryI : virtual public Directory,virtual public NodeI {
public:
virtual NodeSeq list(const Ice::Current&);
DirectoryI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&);
void addChild(const Filesystem::NodePrx&);
private:
Filesystem::NodeSeq _contents;
};
}
slice2cpp --impl 生成文件系统使用了每个类的实现继承,如下图所示:
⑴、NodeI
NodeI是具体基类,从Node类继承name操作;FileI,DirectoryI采用了多继承。实现NodeI,可以由FileI,DirectoryI复用;如果FileI,DirectoryI只单继承各自的File,Directory类,则需要分别实现各项公共操作——File,Directory,Node由slice2cpp生成的映射类,不能直接实现公共操作。
NodeI禁止复制构造和赋值,就会使所有继承类也禁止复制构造和赋值。
NodeI成员_name保存节点名称,_parent保存父目录指针,_id保存当前对象的标识。构造函数:
NodeI(const Ice::CommunicatorPtr&, const std::string&, const DirectoryIPtr&);
创建节点时初始化节点名称和父目录,传入通讯器,用于为服务者创建标识(当前实现中未使用这种方法,而是使用了UUID)。
⑵、FileI
Lines类型变量_lines存储文件内容,该类型定义于Filesystem.h中,不用查看该文件,根据slice规范就应该知道Lines为std::vector<std::string>类型——这就需要完全掌握Slice到C++映射的规则,才能帮助你使用和开发Ice应用程序。
⑶、DirectoryI
每个目录都要存储子目录、文件列表,这里使用了NodeSeq类型的_contents变量。根据slice规范定义sequence<Node*> NodeSeq;可知NodeSeq是vector<NodePrx>,每个元素都是指向一个节点的代理。addChild函数用于将子节点加入到NodeSeq序列中。list函数则列出NodeSeq序列中的名称。
3.3 服务者类实现
#include <IceUtil/IceUtil.h>
#include <FilesystemI.h>
using namespace std;
//————————————————————————————————————
// NodeI
//————————————————————————————————————
std::string
Filesystem::NodeI::name(const Ice::Current&)
{
return _name;
}
// NodeI 构造器
Filesystem::NodeI::NodeI(const Ice::CommunicatorPtr& communicator, const string& name, const DirectoryIPtr& parent) : _name(name), _parent(parent)
{
// 创建标识,根目录具有固定标识 "RootDir",非根目录使用UUID,把标识保存到_id成员中。
if(parent)
{
_id.name = IceUtil::generateUUID();
}
else
{
_id.name = "RootDir";
}
}
// NodeI activate() 成员函数
// 创建节点后,调用本函数,NodeI调用适配器的add操作把自身加入到ASM中,
// 并将add返回的代理转化为NodePrx类型,调用addChild添加到它父节点_contents列表中。
// 如果父节点不存在,则什么也不做。
void
Filesystem::NodeI::activate(const Ice::ObjectAdapterPtr& a)
{
NodePrx thisNode = NodePrx::uncheckedCast(a->add(this, _id));
if(_parent)
{
_parent->addChild(thisNode);
}
}
//————————————————————————————————————
// FileI
//————————————————————————————————————
Filesystem::Lines
Filesystem::FileI::read(const Ice::Current&)
{
return _lines; //读取内容
}
void
Filesystem::FileI::write(const Filesystem::Lines& text, const Ice::Current&)
{
_lines = text; //写入内容
}
// FileI 构造器,只是将参数简单地传给NodeI基类
Filesystem::FileI::FileI(const Ice::CommunicatorPtr& communicator, const string& name, const DirectoryIPtr& parent) : NodeI(communicator, name, parent)
{
}
//————————————————————————————————————
// Directory
//————————————————————————————————————
Filesystem::NodeSeq
Filesystem::DirectoryI::list(const Ice::Current& c)
{
return _contents;
}
// DirectoryI 构造器,只是将参数简单地传给NodeI基类
Filesystem::DirectoryI::DirectoryI(const Ice::CommunicatorPtr& communicator, const string& name,const DirectoryIPtr& parent) : NodeI(communicator, name, parent)
{
}
// addChild 由子节点调用以便将自身添加到父目录的_contents成员
void
Filesystem::DirectoryI::addChild(const NodePrx& child)
{
_contents.push_back(child); //vector
}
4、客户端
#include <Ice/Ice.h>
#include <Filesystem.h>
#include <iostream>
#include <iterator>
using namespace std;
using namespace Filesystem;
// 以树形风格递归打印目录dir的内容。
// 对于文件,则显示每个文件的内容。
// "depth"参数是当前嵌套层次(用于缩进)。
static void
listRecursive(const DirectoryPrx& dir, int depth = 0)
{
string indent(++depth, '/t'); //缩进
NodeSeq contents = dir->list(); //dir目录下的所有子节点
for (NodeSeq::const_iterator i = contents.begin(); i != contents.end(); ++i) {
DirectoryPrx dir = DirectoryPrx::checkedCast(*i);
FilePrx file = FilePrx::uncheckedCast(*i);
cout << indent << (*i)->name() << (dir ? " (directory):" : " (file):") << endl;
if (dir) {
listRecursive(dir, depth);
} else {
Lines text = file->read();
for (Lines::const_iterator j = text.begin(); j != text.end(); ++j)
cout << indent << "/t" << *j << endl;
}
}
}
int
main(int argc, char* argv[])
{
int status = 0;
Ice::CommunicatorPtr ic;
try {
// 创建通讯器
ic = Ice::initialize(argc, argv);
// 为根目录创建代理
Ice::ObjectPrx base = ic->stringToProxy("RootDir:default -p 10000");
if (!base)
throw "Could not create proxy";
// 将代理向下转型为Directory代理
DirectoryPrx rootDir = DirectoryPrx::checkedCast(base);
if (!rootDir)
throw "Invalid proxy";
// 递归列出根目录的内容
cout << "Contents of root directory:" << endl;
listRecursive(rootDir);
} catch (const Ice::Exception & ex) {
cerr << ex << endl;
status = 1;
} catch (const char * msg) {
cerr << msg << endl;
status = 1;
}
// 清除
if (ic)
ic->destroy();
return status;
}
客户端文件:
Filesystem.h
Filesystem.cpp
Client.cpp
在初始化运行时之后,客户创建一个代理,指向文件系统的根目录。端口为"RootDir:default -p 10000",使用缺省协议(TCP)在10000 端口处侦听。根目录的对象标识叫作RootDir。客户把代理向下转换成DirectoryPrx,并把这个代理传给listRecursive,由它打印出文件系统的内容。
listRecursive 完成了主要工作。它接收目录代理为参数,以及一个缩进层次参数(缩进每次递归调用就增加一层,使得缩进与节点深度对应)。listRecursive 调用目录的list 操作,并且遍历所返回的节点序列:
⑴、代码调用checkedCast,把Node代理窄化成Directory 代理;并且调用uncheckedCast,把Node代理窄化成File代理。在这两个转换中只有、而且肯定会有一个成功,所以不需要两次调用checkedCast:如果节点是Directory,代理就是checkedCast返回的DirectoryPrx;如果checkedCast失败,节点就一定是File,只要使用uncheckedCast就可以正确的获得File。一般而言,如果向下转型肯定能成功的话,最好使用uncheckedCast,而不是checkedCast。
⑵、根据节点性质,打印文件/目录名字。
⑶、如果是目录,代码就会递归调用listRecursive,增加缩进层次;如果是文件,就调用文件的read 操作,取回文件内容序列,遍历打印内容行序列。
⑷、根据服务器端的构造,我们可以在客户端产生如下的输出:
Contents of root directory:
README (file):
This file system contains a collection of poetry.
Coleridge (directory):
Kubla_Khan (file):
In Xanadu did Kubla Khan
A stately pleasure-dome decree:
Where Alph, the sacred river, ran
Through caverns measureless to man
Down to a sunless sea.
5、总结
现在,我们实现了文件系统的Ice应用程序,但是我们应用程序还有一些问题:
⑴、端点信息硬编码到程序中;
⑵、客户进行了一些不必要的远程调用;
⑶、不支持并发,两个客户同时读、写同一个文件,会产生冲突。
要解决这些问题,需要学习DPWI第4部分高级Ice。通过对Ice的深入了解,可以实现高性能、高并发的Ice应用程序。
相关推荐
ICE-3.7.4 最新安装文件msi文件,windows版 ICE常见报错 Exception in thread Ice.ConnectionRefusedException error = 0 at IceInternal.Network.doFinishConnect(Network.java:417) at IceInternal....
本篇文章将详细探讨如何基于ICE中间件实现文件传输功能。 首先,我们要理解ICE的核心概念。ICE支持多种编程语言,如C++、Java、Python等,它提供了一种透明的接口,让开发者可以在不同的网络节点之间进行对象交互,...
ICE客户端实现代码主要涉及到的是Tibco的ICE(Internet Communications Engine)中间件的使用,这是一种高性能、可伸缩的分布式计算框架。ICE提供了一种语言无关、平台无关的方式来构建分布式应用,支持C++、Java、...
因此,在许多VI-SLAM应用中,为了实现快速的实时位姿解算,系统性能只能达到次优。为了解决这一问题,ICE-BA(Incremental, Consistent and Efficient Bundle Adjustment for Visual-Inertial SLAM)提供了一种新颖...
iCE40系列:超低功耗FPGA评估和开发方案.pdf
对于空间狭小的移动设备而言,这能够帮助设计者减少PCB(印刷电路板)上的占用空间,实现更小型化的设计。小型化设计不仅有助于减轻设备重量,还可以提高设备的集成度和可靠性。 再者,iCE40 Ultra的编程和配置的...
基于ICE中间件的分布数据处理系统设计与实现_刘欢.caj基于ICE中间件的分布数据处理系统设计与实现_刘欢.caj
在C++编程环境中,ICE为开发者提供了一套强大的工具和库,使得构建分布式应用程序变得更加简单。本示例程序是基于ICE的C++实现,适用于Visual Studio 2008开发环境,非常适合初学者学习和理解ICE框架的基本用法。 ...
11.2 实现文件系统服务器 261 11.3 总结 276 第 12 章 服务器端的 Slice-to-Java 映射 279 12.1 Chapter Overview 279 12.2 引言 279 12.3 服务器端 main函数 280 12.4 接口的映射 285 12.5 参数传递 287 12.6 引发...
该项目提供了ICE协议的Java实现,该协议可由SIP和XMPP应用程序使用。 该项目还提供了套接字共享等功能,并支持Pseudo TCP。 ice4j由社区维护。 使用Jitsi的进行问题和讨论。 谢谢 该项目的工作由慷慨资助。 谢谢!
书中的示例包括了一个简单的文件系统的Slice定义,为读者提供了如何从头到尾设计一个分布式文件系统应用的实例。书中还对Slice定义和CORBA的IDL(接口定义语言)进行了对比,加深了读者对ICE特有的分布式对象模型的...
以上是对《Ice 分布式程序设计》这一文档的主要知识点的概括,该书深入浅出地介绍了 Ice 分布式程序设计的基本概念、核心技术和应用场景,对于希望掌握分布式系统开发的技术人员来说是一本宝贵的资源。
标题中的“ice最简单实现 ruby调用ice接口”是指在Ruby编程语言中使用ICE(Internet Communication Engine)框架来实现远程方法调用(RPC)。ICE是由ZeroC开发的一种跨平台、高性能的中间件,它允许不同语言的应用...
Ice 的核心设计原则之一是简化复杂系统的开发过程,使得开发者能够专注于业务逻辑而不是低级通信细节。 #### 2. Ice 架构 ##### 2.1 架构概述 Ice 的架构设计非常灵活,可以支持多种操作系统和编程语言。它主要由...
ZeroC ICE集群搭建 ICE安装目录: /home/apps/cpplibs...通常会存在多台机器,每台机器上面启动一个icegridnode, 文件node.id指定了icegridnode的Name,一般文件内容为hostname(ctrl.sh会取hostname来操作当前文件夹下的
以上示例展示了如何使用ICE构建一个简单的客户端-服务器系统,其中包含了ICE的安装步骤、基本使用流程以及客户端和服务端的实现。ICE的强大之处在于它能够简化网络编程的复杂度,使得开发者可以更加专注于业务逻辑的...