【 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中间件的分布数据处理系统设计与实现_刘欢.caj基于ICE中间件的分布数据处理系统设计与实现_刘欢.caj
ICE-BA: Incremental, Consistent and Efficient Bundle Adjustment for Visual-Inertial SLAM的pdf文档
iCE40 Ultra:移动设备中的杀手级芯片.pdf
基于ICE中间件的文件传输方法的设计与实现,李琦,陈伟,石油,电力,银行等行业是国家的国民经济的重要部门,其系统组织结构复杂,部门繁多。要维持整个系统的高效准确的运行,必须对很
ICE-3.7.4 最新安装文件msi文件,windows版 ICE常见报错 Exception in thread Ice.ConnectionRefusedException error = 0 at IceInternal.Network.doFinishConnect(Network.java:417) at IceInternal....
iCE40系列:超低功耗FPGA评估和开发方案.pdf
基于ICE中间件实现的传文件 InstallService UpdateService dllICE
第 5 章 一个简单文件系统的 Slice 定义 第 6 章 客户端的 Slice-to-C++ 映射 第 7 章开发 C++ 文件系统客户 第 8 章 客户端的 Slice-to-Java 映射 第 9 章开发 Java 文件系统客户 第 10 章 服务器端的 Slice-to-C++...
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的进行问题和讨论。 谢谢 该项目的工作由慷慨资助。 谢谢!
ZeroC ICE集群搭建 ICE安装目录: /home/apps/cpplibs...通常会存在多台机器,每台机器上面启动一个icegridnode, 文件node.id指定了icegridnode的Name,一般文件内容为hostname(ctrl.sh会取hostname来操作当前文件夹下的
ICE-3.7.1最新安装文件msi文件,绝对官方。 RPC王者。
ice 的样例文件
这是我从中国优秀硕士学位论文全文数据库下载的优秀硕士学位论文《大型联网视频监控系统中ICE中间件的设计与实现》,如果文件格式为*.kdh或者*.nh,请到中国知网下载阅读阅读器CAJViewer, 网址为...
Ice 是一种面向对象的中间件平台。从根本上说,这意味着Ice 为构建面 向对象的客户-服务器应用提供了工具、API 和库支持。Ice 应用适合在异 种环境中使用:客户和服务器可以... 本例子是用java写的ice实现的HelloWorld
详细介绍了ICE的各种知识功能的使用以及一些c++和java的代码例子
icecream-cpp::soft_ice_cream:永远不要使用coutprintf进行调试
框架实现了基于WebRTC的视频会议系统,总体包括服务端和客户端的设计与实现。服 务端包括媒体服务器、信令服务器、防火墙打洞服务器。其中媒体服务器釆用SFU架构 进行实现,信令服务器采用...
ICE-3.7.1最新帮助文件,官方文件,最新加载。欢迎大家下载
xmake-idea::ice_cream:IntelliJ IDEA中的XMake集成