基於Ice 3.7.2實現C#與C#,C#與JAVA方法的相互調用

本文章代碼示例下載地址:

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.exeslice2java.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#類庫

  1. 新建一個空白解決方案,命名爲ICETest,在空白解決方案處,新建一個項目,類型選擇爲類庫,將Hello.cs放進去

  2. 在Nuget中添加ICE引用:

在這裏插入圖片描述

  1. 編譯排錯,能編譯通過說明沒問題了:

    在這裏插入圖片描述

編寫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);
            }
        }
    }
}

最後,通訊界面如下:

在這裏插入圖片描述

本文章代碼示例下載地址:

https://github.com/lishuangquan1987/ICETest

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章