现在的位置: 首页 > 综合 > 正文

ICE C++ Hello World

2013年12月06日 ⁄ 综合 ⁄ 共 4729字 ⁄ 字号 评论关闭

ICE C++ Hello World实例教程

1.  概述

本文演示了如何编写一个最简单的C++ICE(Internet Communications Engine)应用程序,包括必要环境的安装。该应用程序包含客户端和服务端两部分。当服务端正常运行时,运行客户端,将在服务端的标准输出上输出“Hello World”。

 

2.  安装Ubuntu

安装一份Ubuntu系统,可以是在物理机上安装,也可以在虚拟机上安装。本文中使用的Ubuntu版本为12.10

 

3. 安装ICE环境

进入Ubuntu桌面环境,按Ctrl + Alt + T打开终端界面,输入下面的命令

sudo apt-get install zeroc-ice34

此命令将会自动安装ICE环境。等待安装完成后,可以尝试运行slice2cpp命令,如果出现该命令的提示,则说明ICE环境已经成功安装。

ICE默认安装在/usr目录下,进入/usr目录,在include和lib目录中可以看到ICE相关的头文件目录以及动态链接库

4. 安装g++ 4.4

在编写C++ ICE程序时,需要使用g++进行编译。不过最新的g++版本太高,在编译时会有问题,ICE的官方文档中说明最高支持的g++版本为4.4,因此需要安装g++ 4.4版本。

sudo apt-get install g++-4.4

安装完成后,运行g++,显示找不到文件,这是由于系统中没有设置该命令指向的文件。使用以下命令建立命令和文件之间的关联。同时将c++命令也关联到该文件

sudo update-alternatives --install /usr/bin/g++ g++/usr/bin/g++-4.4 40

sudo update-alternatives --install /usr/bin/c++ c++/usr/bin/g++-4.4 40

(关于这条命令的详细信息,请参考 update-alternatives的相关说明)

运行完上面命令后,再次运行g++,则出现了g++的提示。查看一下g++的版本:

g++ -v或 c++ -v

可以发现,g++确实为4.4版本

 

5. 定义接口文件

编写ICE应用程序的第一步是定义slice文件。为了支持客户端和服务端使用不同的编程语言来实现,ICE定义了一种独立于各种开发语言,用来描述服务端提供的接口的语言,即slice。客户端根据slice文件可以知道服务端有哪些服务可以提供,并且知道应该以什么样的方式来使用服务端提供的服务。服务端则在slice文件定义的接口基础上去实现接口的功能。

新建一个目录,例如icehelloworld,将下面的内容保存为文件Printer.ice

module Demo {
interface Printer {
void printString(string s);
};
};


在这段文本中,定义了一个模块Demo。在此模块中有一个接口Printer。由于HelloWorld程序只需要服务端提供一个最简单的输出功能,因此这个接口目前也非常简单,只提供一个方法printString。该方法接收一个字符串类型的参数s。

 

6. 编写C++服务端

当使用具体的开发语言进行客户端或服务端的开发时,slice文件需要被转换为对应的开发语言能够使用的代码。ICE提供了这样的转换工具:

slice2cpp:  将slice文件转换为c++代码

运行以下命令,将slice文件转换为c++代码

slice2cpp Printer.ice

可以发现目录下多了两个文件:Printer.h和Printer.cpp。

slice转换为c++代码的过程是比较直观的。module将被转换为c++中的namespace,而interface将被转换为一个抽象类,interface中的方法也会被转换为该抽象类中的方法。服务端的代码从该抽象类派生一个具体类,并实现抽象类中的方法,这样就可以提供RPC接口的功能。远程过程调用

接下来就可以开始写服务端的代码了。新建文件,输入以下代码,保存为Server.cpp。这份服务代码看起来似乎比一个简单的HelloWorld程序要复杂很多,但是其中大部分代码都都是一些标准的初始化过程,真正与具体服务功能相关的代码只有几行而已。代码中的注释详细解释了代码的内容。

// 所有的ICE程序都需要包含ice.h, 这个文件包含了ICE的各种资源
#include <Ice/Ice.h> 
 
// 这个是前面的slice文件生成的c++头文件,包含了服务端提供的接口信息
#include <Printer.h>
 
using namespace std;
using namespace Demo; // Printer.h 中生成的命名空间
 
// 从Printer抽象类派生
class PrinterI : public Printer {
public:
//ICE接口方法中都会自动增加一个参数 Ice::Current,不过在这个HelloWorld程序中我们不需要用到
virtual void printString(const string& s, const Ice::Current&);
};
 
// 这里就是RPC接口的实现代码
void PrinterI::printString(const string& s, const Ice::Current&)
{
// 这个接口实现的功能非常简单:在标准输出上打印接收到的字符串
cout << s << endl;
}
 
// 服务端将从main函数开始运行
int main(int argc, char* argv[])
{
int status = 0;  //退出状态
//ic是一个指向ICE运行时资源的智能指针,通过ic可以获取运行时的各种资源。
Ice::CommunicatorPtr ic;
try {
//  Ice::initialize 初始化一个ICE运行时。传入 argc,argv是因为服务代码可能会处理命令行参数(本例中不需要)。
ic = Ice::initialize(argc, argv);
// 创建一个 ObjectAdapterPtr adapter,名字为 SimplePrinterAdapter。这个Adapter监听TCP/IP的10000端口
Ice::ObjectAdapterPtr adapter =
ic->createObjectAdapterWithEndpoints("SimplePrinterAdapter","tcp -p 10000");
 
// 实例化一个PrinterI对象,该对象将为接口Printer提供服务
Ice::ObjectPtr object = new PrinterI;
 
// 把PrinterI对象加入ObjectAdapter,标识名为SimplePrinter。当有客户端请求Printer的服务时,ObjectAdapter将会把请求转给PrinterI对象
adapter->add(object, ic->stringToIdentity("SimplePrinter"));
 
// 启动ObjectAdapter, 此后ObjectAdapter开始处理实际的调用请求
adapter->activate();
 
// 阻塞主线程,直到服务端的运行时被关闭
ic->waitForShutdown();
 
} catch (const Ice::Exception& e) {
cerr << e << endl;
status = 1;
} catch (const char* msg) {
cerr << msg << endl;
status = 1;
}
 
// 程序结束时,需要销毁ICE运行时资源。如果在程序退出时没有对ICE运行时进行销毁,可能引起未知错误
if (ic) {
try {
ic->destroy();
} catch (const Ice::Exception& e) {
cerr << e << endl;
status = 1;
}
}
  returnstatus;
}

编译服务端代码。在编译和连接时需要指定ICE头文件和动态链接库的位置

c++-I. -I/usr/include -c Printer.cpp Server.cpp

c++-o server Printer.o Server.o -L/usr/lib -lIce -lIceUtil

编译完成后,将在当前目录生成一个名为server的可执行文件

 

7. 编写C++客户端

        新建一个文件,取名为Client.cpp,输入以下代码。

#include <Ice/Ice.h>
#include <Printer.h>
 
using namespace std;
using namespace Demo;
 
// 客户端只需要一个main函数,并且代码结构与服务端代码类似
int main(int argc, char* argv[])
{
int status = 0;
Ice::CommunicatorPtr ic;
try {
// 初始化ICE运行时
ic = Ice::initialize(argc, argv);
 
// 使用字符串来生成一个对象代理
// 对象代理包含有服务端的服务对象的方法定义,并且负责和服务端的对象进行通信。因此客户端可以像使用本地对象一样使用服务端的对象
// 字符串中指定了对象代理对应的服务端的对象的名称,以及服务端对象监听的协议和端口。这些信息需要跟服务端中定义的信息一致
//ObjectPrx 在客户端代理服务器端
Ice::ObjectPrxbase = ic->stringToProxy("SimplePrinter:tcp -p 10000");
// 上面得到的是一个范型的对象代理,需要将它转换为Printer对象的代理,这样才能调用printString方法。
// checkedCast会与服务端进行通信,以判断该对象代理能否成功转换为Printer对象代理。如果转换失败,将返回空对象代理
PrinterPrx printer =PrinterPrx::checkedCast(base);
if (!printer)
throw "Invalid proxy";
// 调用Printer对象代理的printString方法。调用将会通过对象代理被发送到服务端
printer->printString("HelloWorld!");
} 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;
}

编译客户端代码,同样需要指定ICE头文件路径和动态链接库的位置

c++-I. -I/usr/include -c Printer.cpp Client.cpp

c++-o client Printer.o Client.o -L/usr/lib -lIce -lIceUtil

编译完成后,将在当前目录生成一个名为client的可执行文件

 

8. 测试

在当前目录运行 server

./server  

成功运行后不会有任何输出

Crtl + Alt + T 新开一个终端,进入到之前的目录,运行client

./client

成功运行后同样不会有任何输出,并且程序会立即退出。

切换到运行server的终端,可以看到终端上输出了“Hello World”

 

9. 相关资源

ICE官方网站

http://www.zeroc.com

ICE源码下载

http://www.zeroc.com/download.html

文档中心(用户手册在线版)

http://doc.zeroc.com/display/Doc/Home

用户手册(PDF)

http://doc.zeroc.com/display/Ice/Ice+Manual

抱歉!评论已关闭.