基于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

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