本文章代码示例下载地址:
https://github.com/lishuangquan1987/ICETest
官网:https://zeroc.com/downloads/ice/3.7
文档:https://doc.zeroc.com/ice/3.7/release-notes/supported-platforms-for-ice-3-7-3
开源地址:https://github.com/zeroc-ice/ice
ICE是远程方法调用RPC(Romote Procedure Call)的一种实现方式,支持多种语言,支持多种平台
支持的语言:
支持的平台
由于Ice3.7没有相应的MSI安装包,只有自己下载源码编译(Ice3.6是有MSI安装包的)
下载源码,编译slice2cs.exe,slice2java.exe,因为我们需要c#与java通讯,要将同一个slice文件生成java和c#的接口
我们进入到cpp\msbuild文件夹,用VS2019打开ice.v142.sln
在解决方案视图中,右键单击C++98文件夹,选择生成选项,即可生成所需要的exe:
生成后的exe如下:
有关slice语言的语法规则,请参看说明书:https://doc.zeroc.com/ice/3.7/the-slice-language
首先,我们编写一个Slice文件为Hello.ice
,要实现C#与Java的相互调用,必须有两个接口,一个客户端调用服务端的接口,一个服务端回调客户端的接口:
module TestInterface1
{
//客户端调用服务端的接口
interface Test
{
void SendMessageToServer(string msg);
void CheckAlive();
//客户端调用服务端完成注册
void Register(string address);
};
};
module TestInterface2
{
//服务端调用客户端的接口
interface TestCallBack
{
void SendMessageToClient(string msg);
void CheckAlive();
};
};
说明一下:两个接口中都有CheckAlive方法,用于检测心跳
客户端调用服务端的Register
方法,用于向服务端注册,告诉服务端,客户单已经建立好让服务端回调客户端的通道,其中adress就是客户端监听的地址
使用用我们刚刚编译出来的slice2cs.exe
和slice2java.exe
首先,打开cmd命令行,把路径cd到我们的Hello.ice
目录:
cd /d F:\Project2019\ICETest
F:\下载的代码\ice-3.7\cpp\bin\Win32\Debug\slice2cs.exe Hello.ice
运行该命令之后,会在Hello.ice所在的文件夹下生成一个Hello.cs文件:
F:\下载的代码\ice-3.7\cpp\bin\Win32\Debug\slice2java.exe Hello.ice
运行该命令之后,会在Hello.ice所在的文件夹下生成两个文件夹:TestInterface1和TestInterface2,这两个文件夹的名称就是slice文件的module名称:
说明:本文C#代码都是在.Net Framework4.7.2
上写的
为了能够代码重用,将生成的Hello.cs
文件单独放到一个类库中,服务端和客户端都引用这个类库
新建一个空白解决方案,命名为ICETest,在空白解决方案处,新建一个项目,类型选择为类库,将Hello.cs放进去
在Nuget中添加ICE引用:
在解决方案下再新建一个控制台项目,命名为ICEServer,添加对TestInterface的引用,在Nuget中添加对ICE的引用。
新建一个类,继承自Hello.cs中的TestDisp_
类,此类是slice文件自动生成的,是一个抽象类,我们只需要实现里面的抽象方法就好了:
public class TestImplement : TestDisp_
{
public event Action<string> RegisterEvent;
int count = 0;
public override void CheckAlive(Current current = null)
{
count++;
Console.WriteLine("check alive:"+count);
}
public override void SendMessageToServer(string msg, Current current = null)
{
Console.WriteLine("收到消息:"+msg);
}
public override void Register(string address, Current current = null)
{
if (RegisterEvent != null)
{
RegisterEvent(address);
}
}
}
static void Main(string[] args)
{
using (var communicator = Ice.Util.initialize())
{
//储存连接的客户端
List<TestInterface2.TestCallBackPrx> clients = new List<TestInterface2.TestCallBackPrx>();
//创建一个适配器,其中"test"名称随便写
var adapter = communicator.createObjectAdapterWithEndpoints(
"test", "default -h localhost -p 10005");
TestImplement testImplement = new TestImplement();
//当客户端调用Register方法是会触发RegisterEvent事件,此时服务端应该在该处理事件中建立调用客户单的代理管道,
//以保证服务端可以主动调用客户端的方法
testImplement.RegisterEvent += address =>
{
//注意:地址前面加上client:是因为要与客户端的Indentity名称一致
var proxy = communicator.stringToProxy("client:" + address);
Console.WriteLine("address:"+address+"连接上了服务器。");
TestInterface2.TestCallBackPrx client = TestInterface2.TestCallBackPrxHelper.checkedCast(proxy);
clients.Add(client);
};
//这里的服务端指定的Indetntity名称"testIndentity",客户端连接服务端时,必须在地址前面加上此标识才能连接
adapter.add(testImplement, communicator.stringToIdentity("testIndentity"));
//启动服务,等待客户端连接
adapter.activate();
Console.WriteLine("按住Ctrl+C结束...");
Console.CancelKeyPress += (sender, e) => Environment.Exit(Environment.ExitCode);
while (true)
{
Console.WriteLine("请输入信息:发送心跳请输入0,按回车发送");
string msg = Console.ReadLine();
if (msg == "0")
{
clients.ForEach(x => x.CheckAlive());
}
else
{
clients.ForEach(x => x.SendMessageToClient(msg));
}
}
}
}
至此,服务端代码编写完成
在解决方案处再新建一个控制台项目,命名为ICEClient.
编写一个类,继承自Hello.cs中的TestInterface2.TestCallBackDisp_
类,此类也是slice文件生成的,是一个抽象类,我们只需要实现其抽象方法:
class ClientImplement : TestInterface2.TestCallBackDisp_
{
public override void CheckAlive(Current current = null)
{
Console.WriteLine("check alive");
}
public override void SendMessageToClient(string msg, Current current = null)
{
Console.WriteLine("收到消息:"+msg);
}
}
在Main中编写客户端代码:
static void Main(string[] args)
{
using (var communicator = Ice.Util.initialize())
{
//此处的testIndentity:与服务端的Identity一致,否则连接不到服务器
var proxy = communicator.stringToProxy("testIndentity:default -h localhost -p 10005");
var instance = TestInterface1.TestPrxHelper.checkedCast(proxy);
//创建adapter监听端口
string address = "default -h localhost -p 10006";
var adapter = communicator.createObjectAdapterWithEndpoints("client", address);
var callback = new ClientImplement();
adapter.add(callback,new Identity() { name= "client" });
//启动监听
adapter.activate();
//告知服务端客户端已经监听的地址,服务端可以连接到客户端
instance.Register(address);
Console.WriteLine("按住Ctrl+C退出.");
while (true)
{
Console.WriteLine("请输入发给客户端的消息,心跳输入0,按Enter发送");
string msg = Console.ReadLine();
if (msg == "0")
{
instance.CheckAlive();
}
else
{
instance.SendMessageToServer(msg);
}
}
}
}
最后,整个项目的目录结构如下:
到这里,客户端与服务端都可以进行相互通讯了:
使用IDEA新建一个Java Maven项目:
建好之后,在java目录下建一个com.Test2包,将之前生成的TestInterface1,TestInterface2文件夹及其里面的java文件全部复制到包下,目录结构如下:
在pom.xml中添加对ICE的Maven依赖:
<dependency>
<groupId>com.zerocgroupId>
<artifactId>iceartifactId>
<version>3.7.3version>
dependency>
package com.Test2;
import com.Test2.TestInterface2.TestCallBack;
import com.Test2.TestInterface2._TestCallBackPrxI;
import com.zeroc.Ice.Current;
public class ClientImplement implements TestCallBack {
@Override
public void SendMessageToClient(String msg, Current current) {
System.out.println("收到消息:"+msg);
}
@Override
public void CheckAlive(Current current) {
System.out.println("check alive");
}
}
编写Main函数(与C#编写客户端一样):
package com.Test2;
import com.Test2.TestInterface1.TestPrx;
import com.zeroc.Ice.*;
import com.zeroc.Ice.Object;
import java.util.Scanner;
public class Main {
public static void main(String[] args)
{
Communicator communicator = Util.initialize();
ObjectPrx objectPrx = communicator.stringToProxy("testIndentity:default -h localhost -p 10005");
TestPrx testPrx = TestPrx.checkedCast(objectPrx);
String address="default -h localhost -p 10007";
ObjectAdapter adapter = communicator.createObjectAdapterWithEndpoints("client", address);
ClientImplement clientImplement=new ClientImplement();
Identity identity=new Identity();
identity.name="client";
adapter.add((Object) clientImplement,identity);
adapter.activate();
testPrx.Register(address);
Scanner scanner=new Scanner(System.in);
while (true)
{
System.out.println("请输入要发送的字符串,心跳请发送0");
String msg=scanner.nextLine();
if (msg.equals("0"))
{
testPrx.CheckAlive();
}
else
{
testPrx.SendMessageToServer(msg);
}
}
}
}
最后,通讯界面如下:
本文章代码示例下载地址:
https://github.com/lishuangquan1987/ICETest