本文章代碼示例下載地址:
https://github.com/lishuangquan1987/ICETest
ICE
官網: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)的一種實現方式,支持多種語言,支持多種平臺
支持的語言:
- c++
- c#
- java
- javascript
- python
- php
- matlab
- objective-c
- ruby
- swift
- typescript
支持的平臺
- Android
- iOS
- Linux
- Linux on embedded devices
- macOS
- Node.js
- Unix systems such as AIX
- Web Browser
- Windows
Ice 3.7.2的源碼下載
由於Ice3.7沒有相應的MSI安裝包,只有自己下載源碼編譯(Ice3.6是有MSI安裝包的)
下載源碼,編譯slice2cs.exe,slice2java.exe,因爲我們需要c#與java通訊,要將同一個slice文件生成java和c#的接口
去github上把ice的源碼下載下來,解壓
我們進入到cpp\msbuild文件夾,用VS2019打開ice.v142.sln
在解決方案視圖中,右鍵單擊C++98文件夾,選擇生成選項,即可生成所需要的exe:
生成後的exe如下:
編寫Slice接口文件
有關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就是客戶端監聽的地址
編譯Hello.ice
使用用我們剛剛編譯出來的slice2cs.exe
和slice2java.exe
首先,打開cmd命令行,把路徑cd到我們的Hello.ice
目錄:
cd /d F:\Project2019\ICETest
生成c#接口
F:\下載的代碼\ice-3.7\cpp\bin\Win32\Debug\slice2cs.exe Hello.ice
運行該命令之後,會在Hello.ice所在的文件夾下生成一個Hello.cs文件:
生成Java接口
F:\下載的代碼\ice-3.7\cpp\bin\Win32\Debug\slice2java.exe Hello.ice
運行該命令之後,會在Hello.ice所在的文件夾下生成兩個文件夾:TestInterface1和TestInterface2,這兩個文件夾的名稱就是slice文件的module名稱:
編寫C#端代碼
說明:本文C#代碼都是在.Net Framework4.7.2
上寫的
爲了能夠代碼重用,將生成的Hello.cs
文件單獨放到一個類庫中,服務端和客戶端都引用這個類庫
編寫C#類庫
-
新建一個空白解決方案,命名爲ICETest,在空白解決方案處,新建一個項目,類型選擇爲類庫,將Hello.cs放進去
-
在Nuget中添加ICE引用:
-
編譯排錯,能編譯通過說明沒問題了:
編寫C#服務端代碼
在解決方案下再新建一個控制檯項目,命名爲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);
}
}
}
在Main中編寫服務端代碼
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));
}
}
}
}
至此,服務端代碼編寫完成
編寫C#客戶端代碼
在解決方案處再新建一個控制檯項目,命名爲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);
}
}
}
}
最後,整個項目的目錄結構如下:
到這裏,客戶端與服務端都可以進行相互通訊了:
編寫Java客戶端代碼
使用IDEA新建一個Java Maven項目:
建好之後,在java目錄下建一個com.Test2包,將之前生成的TestInterface1,TestInterface2文件夾及其裏面的java文件全部複製到包下,目錄結構如下:
在pom.xml中添加對ICE的Maven依賴:
<!-- https://mvnrepository.com/artifact/com.zeroc/ice -->
<dependency>
<groupId>com.zeroc</groupId>
<artifactId>ice</artifactId>
<version>3.7.3</version>
</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);
}
}
}
}
最後,通訊界面如下:
本文章代碼示例下載地址: