修炼者
修炼者
发布于 2025-10-17 / 26 阅读
0
0

ICE

本文介绍如何在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 files

CMakeLists.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.008s

Java开发服务与客户端

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!

参考资料

  1. ICE官网 C++ https://zeroc.com/ice/downloads/3.7/cpp

  2. 从github获取最新版本 https://github.com/zeroc-ice/ice/releases/tag/v3.7.10


评论