本文介绍如何在ubuntu下,从源码安装ICE并使用C++、Java开发服务和客户端应用。
系统环境
安装ICE需要的库与工具。
sudo apt-get update
sudo apt-get install libmcpp-dev
sudo apt install liblmdb-dev
sudo apt install libedit-dev构建与安装步骤
下载源码
# 从GitHub克隆,或者直接下载指定版本的release
# git clone -b 3.7.10 https://github.com/zeroc-ice/ice.git
wget https://github.com/zeroc-ice/ice/archive/refs/tags/v3.7.10.tar.gz
tar zxvf v3.7.10.tar.gz
cd ice-3.7.10修改安装路径
目前,最新版本(3.7.10)的ICE, C++部分默认安装到/opt下,可以通过config/Make.rules修改。
#prefix ?= /opt/Ice-$(version)
prefix ?= /home/gaoyong/soft/Ice-$(version)Java部分可通过java/gradle.properties修改
//
// Select an installation base directory. The directory will be created
// if it does not exist. If this property is not set the default will be
// used. Default values are:
// - Windows: %SystemDrive%\Ice-${iceVersion}
// - Non Windows /opt/Ice-${iceVersion}
//
prefix = /home/gaoyong/soft/Ice-3.7.10
忽略不需要的语言
这里主要保留C++和Java,因此将php、js、ruby等都忽略了,办法是将原来的Makefile改为Makefile.bk,再写一个Makefile替换调这些目录原来的Makefile。例如ruby的:
all:ruby
ruby:
echo "without ruby, if enable, mv Makefile.bk back"
install:
echo "without ruby, if enable, mv Makefile.bk back"
构建并安装
执行如下语句,完成编译和安装。中间可能还有错误,逐个解决就好。
make -j 6
make install完成后,目标目录结构如下:
$ tree -L 2 ice-3.7.10/
ice-3.7.10/
├── bin
│ ├── glacier2router
│ ├── icebox
│ ├── iceboxadmin
│ ├── icebridge
│ ├── icegridadmin
│ ├── icegriddb
│ ├── icegridnode
│ ├── icegridregistry
│ ├── icepatch2calc
│ ├── icepatch2client
│ ├── icepatch2server
│ ├── icestormadmin
│ ├── icestormdb
│ ├── slice2confluence
│ ├── slice2cpp
│ ├── slice2cs
│ ├── slice2html
│ ├── slice2java
│ ├── slice2js
│ ├── slice2matlab
│ ├── slice2objc
│ ├── slice2php
│ ├── slice2py
│ ├── slice2rb
│ └── slice2swift
├── config
│ └── templates.xml
├── ICE_LICENSE
├── include
│ ├── Glacier2
│ ├── Ice
│ ├── IceBox
│ ├── IceGrid
│ ├── IcePatch2
│ ├── IceSSL
│ ├── IceStorm
│ └── IceUtil
├── java
│ └── lib
├── java-compact
│ └── lib
├── lib
│ └── x86_64-linux-gnu
├── LICENSE
├── man
│ └── man1
├── python
│ ├── Glacier2
│ ├── Ice
│ ├── IceBox
│ ├── IceGrid
│ ├── IceMX
│ ├── IcePatch2
│ ├── IcePy.cpython-312-x86_64-linux-gnu.so
│ └── IceStorm
└── slice
├── Glacier2
├── Ice
├── IceBox
├── IceBT
├── IceGrid
├── IceIAP
├── IcePatch2
├── IceSSL
└── IceStorm
38 directories, 29 files
C++开发服务与客户端
编写IDL
module Demo {
interface Echo {
string sayEcho(string s);
};
}实现服务
#include <Ice/Ice.h>
#include "Echo.h"
#include <iostream>
#include <string>
#include <unistd.h>
#include <cstdlib>
using namespace std;
using namespace Demo;
class EchoI : public Echo {
public:
virtual string sayEcho(const string& s, const Ice::Current&) override {
return s;
}
};
// 获取绝对路径
string getAbsolutePath(const string& relativePath) {
char* absolutePath = realpath(relativePath.c_str(), nullptr);
if (absolutePath == nullptr) {
throw runtime_error("无法转换为绝对路径: " + relativePath);
}
string result(absolutePath);
free(absolutePath);
return result;
}
int main(int argc, char* argv[]) {
int status = 0;
Ice::CommunicatorPtr ic;
try {
ic = Ice::initialize(argc, argv);
string endpoint;
if (argc == 3) {
// TCP模式
string ip = argv[1];
string port = argv[2];
endpoint = "tcp -h " + ip + " -p " + port;
cout << "TCP模式: " << ip << ":" << port << endl;
}else if(argc == 2){
string ip = argv[1];
endpoint = "tcp -h " + ip + " -p 10000";
cout << "TCP模式: " << ip << ":10000" << endl;
} else {
endpoint = "tcp -h 127.0.0.1 -p 10000";
cout << "默认TCP模式: 127.0.0.1:10000" << endl;
cout << "用法:" << endl;
cout << " TCP: " << argv[0] << " <ip> <端口>" << endl;
}
cout << "使用端点: " << endpoint << endl;
Ice::ObjectAdapterPtr adapter = ic->createObjectAdapterWithEndpoints(
"EchoAdapter", endpoint);
Ice::ObjectPtr object = new EchoI();
adapter->add(object, ic->stringToIdentity("EchoService"));
adapter->activate();
cout << "服务运行中,按Ctrl+C停止" << endl;
ic->waitForShutdown();
} catch (const Ice::Exception& e) {
cerr << "ICE异常: " << e << endl;
status = 1;
} catch (const exception& e) {
cerr << "错误: " << e.what() << endl;
status = 1;
}
if (ic) {
try {
ic->destroy();
} catch (const Ice::Exception& e) {
cerr << "销毁通信器失败: " << e << endl;
status = 1;
}
}
return status;
}
实现客户端
#include <Ice/Ice.h>
#include "Echo.h"
using namespace std;
using namespace Demo;
int main(int argc, char* argv[]) {
int status = 0;
Ice::CommunicatorPtr ic;
try {
// 初始化ICE通信器
ic = Ice::initialize(argc, argv);
const string host = argc>=2?argv[1]:"127.0.0.1";
const string port = argc>=3?argv[2]:"10000";
const string endpoint = "EchoService:tcp -h " + host + " -p " + port;
cout << "使用端点: " << endpoint << endl;
// 连接到服务端
Ice::ObjectPrx base = ic->stringToProxy(endpoint);
//Ice::ObjectPrx base = ic->stringToProxy("EchoService:default -p 10000");
// 转换为具体的Echo接口代理
EchoPrx echo = EchoPrx::checkedCast(base);
if (!echo) {
throw "Invalid proxy";
}
// 调用服务
string s = "hello";
//cout << "Enter a string: ";
//getline(cin, s);
cout << "Server responded: " << echo->sayEcho(s) << endl;
} catch (const Ice::Exception& e) {
cerr << e << endl;
status = 1;
} catch (const char* msg) {
cerr << msg << endl;
status = 1;
}
// 清理资源
if (ic) {
ic->destroy();
}
return status;
}构建
文件结构如下:
$ tree
.
├── CMakeLists.txt
└── src
├── EchoClient.cpp
├── Echo.ice
└── EchoServer.cpp
2 directories, 4 filesCMakeLists.txt的文件内容如下:
cmake_minimum_required(VERSION 3.10)
project(EchoService)
# 显示编译信息
set(CMAKE_VERBOSE_MAKEFILE ON)
# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# ========================== 手动配置ICE路径 ==========================
# 替换为你的ICE实际安装路径
set(ICE_INSTALL_PREFIX "/home/gaoyong/soft/ice-3.7.10/")
# ICE头文件目录(包含Ice目录和生成的Slice头文件)
include_directories(
${ICE_INSTALL_PREFIX}/include
${CMAKE_BINARY_DIR} # 存放slice2cpp生成的代码
)
# ICE库文件目录
link_directories(${ICE_INSTALL_PREFIX}/lib)
# ========================== Slice文件处理 ==========================
# 手动定义Slice编译器路径
set(SLICE2CPP ${ICE_INSTALL_PREFIX}/bin/slice2cpp)
# 定义Slice文件和输出目录
set(SLICE_FILE ${CMAKE_SOURCE_DIR}/src/Echo.ice)
set(SLICE_OUTPUT_DIR ${CMAKE_BINARY_DIR})
# 生成Slice对应的C++代码
add_custom_command(
OUTPUT ${SLICE_OUTPUT_DIR}/Echo.h ${SLICE_OUTPUT_DIR}/Echo.cpp
COMMAND ${SLICE2CPP} ${SLICE_FILE} --output-dir ${SLICE_OUTPUT_DIR}
MAIN_DEPENDENCY ${SLICE_FILE}
COMMENT "Compiling Echo.ice to C++ code"
)
# 创建一个目标确保Slice代码在编译前生成
add_custom_target(
GenerateSliceCode ALL
DEPENDS ${SLICE_OUTPUT_DIR}/Echo.h ${SLICE_OUTPUT_DIR}/Echo.cpp
)
# ========================== 编译可执行文件 ==========================
# 服务端
add_executable(EchoServer
src/EchoServer.cpp
${SLICE_OUTPUT_DIR}/Echo.cpp
)
# 链接ICE核心库(根据实际安装的库调整)
target_link_libraries(EchoServer Ice)
# 确保先编译Slice代码
add_dependencies(EchoServer GenerateSliceCode)
# 客户端
add_executable(EchoClient
src/EchoClient.cpp
${SLICE_OUTPUT_DIR}/Echo.cpp
)
target_link_libraries(EchoClient Ice)
add_dependencies(EchoClient GenerateSliceCode)
构建
cmake -S . -B build -GNinja
cmake --build build/
测试
$ ./build/EchoServer 127.0.0.1
TCP模式: 127.0.0.1:10000
使用端点: tcp -h 127.0.0.1 -p 10000
服务运行中,按Ctrl+C停止
$ time build/EchoClient
使用端点: EchoService:tcp -h 127.0.0.1 -p 10000
Server responded: hello
real 0m0.013s
user 0m0.005s
sys 0m0.008sJava开发服务与客户端
Java使用gradle构建比较顺手。在创建好工程目录之后,步骤也是一样。
初始化工程
创建如下目录结构
$ tree
.
├── build.gradle
├── settings.gradle
└── src
└── main
├── java
│ └── Demo
│ ├── EchoClient.java
│ └── EchoServer.java
└── slice
└── Echo.ice
编写IDL
$ cat src/main/slice/Echo.ice
module Demo {
interface Echo {
string sayEcho(string s);
};
}实现服务
package Demo;
import com.zeroc.Ice.*;
public class EchoServer implements Echo {
public String sayEcho(String s, Current current) {
System.out.println("Received: " + s);
return "Echo: " + s;
}
public static void main(String[] args) {
try (Communicator communicator = Util.initialize(args)) {
ObjectAdapter adapter = communicator.createObjectAdapterWithEndpoints("EchoAdapter", "default -p 10000");
adapter.add(new EchoServer(), Util.stringToIdentity("Echo"));
adapter.activate();
System.out.println("Server ready");
communicator.waitForShutdown();
}
}
}实现客户端
package Demo;
import com.zeroc.Ice.*;
public class EchoClient {
public static void main(String[] args) {
try (Communicator communicator = Util.initialize(args)) {
EchoPrx echo = EchoPrx.checkedCast(
communicator.stringToProxy("Echo:default -p 10000"));
String response = echo.sayEcho("Hello, Ice!");
System.out.println("Response: " + response);
}
}
}构建
plugins {
id 'java'
id 'application'
}
repositories {
flatDir {
dirs '/home/gaoyong/soft/ice-3.7.10/java/lib'
}
mavenCentral()
}
dependencies {
implementation name: 'ice-3.7.10'
implementation 'com.zeroc:ice:3.7.6'
}
sourceSets {
main {
java {
srcDirs = ['src/main/java', 'build/generated/slice']
}
}
}
application {
mainClassName = 'Demo.EchoServer'
}
task compileSlice(type: Exec) {
commandLine '/home/gaoyong/soft/ice-3.7.10/java/bin/slice2java', '--output-dir', 'build/generated/slice', 'src/main/slice/Echo.ice'
outputs.dir 'build/generated/slice'
}
compileJava.dependsOn compileSlice
// 修复客户端JAR打包任务
task buildClient(type: Jar) {
archiveBaseName = 'client'
manifest {
attributes 'Main-Class': 'Demo.EchoClient'
}
// 打包自己的类
from sourceSets.main.output
// 打包所有依赖库
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
// 解决可能的文件冲突(比如META-INF下的文件)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
// 修复服务端JAR打包任务
task buildServer(type: Jar) {
archiveBaseName = 'server'
manifest {
attributes 'Main-Class': 'Demo.EchoServer'
}
// 打包自己的类
from sourceSets.main.output
// 打包所有依赖库
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
// 解决可能的文件冲突
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
task runServer(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = 'Demo.EchoServer'
}
task runClient(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
mainClass = 'Demo.EchoClient'
}
jar.enabled = false# 构建服务
$ ./gradlew build buildServer
# 构建客户端
$ ./gradlew build buildClient测试
# 运行服务
$ java -jar build/libs/server.jar
Server ready
# 运行客户端
$ java -jar build/libs/client.jar
Response: Echo: Hello, Ice!
参考资料
ICE官网 C++ https://zeroc.com/ice/downloads/3.7/cpp
从github获取最新版本 https://github.com/zeroc-ice/ice/releases/tag/v3.7.10