如何通过Remoting实现双向通信

简介:

Remoting是NET平台下比较成熟高效的分布式技术,我们习惯采用传统的远程调用的方式使用Remoting。在客户端所在的Application Domain,我们通过Proxy(Transparent Proxy)远程地跨Application Domain调用一个方法。当来自Client端的调用请求通过Proxy到达Server端所在的Application Domain后,Remoting Infrastructure在Server 端激活(Activate)相应的远程对象(一个继承子System.MarshalByRefObject类对象)——这里仅仅以服务端激活对象(Server Activated Object——SAO),然后再Server端执行相应的操作后把Result传递给Proxy,并最终到达Client。这是一种典型的Request/Response的调用方式。

我之所以一直比较推崇在.NET平台下使用Remoting而非XML Web Service是因为我觉得.NET Remoting是一种比较成熟的分布式技术。它自身提供了XML Web Service很多不具备的特性,其中对双向通信的支持就是一个很好的体现。

相对于典型的Request/Response的消息交换模式(Message Exchange Pattern——MEP),双向通信实质上是采用的Duplex的MEP。也就是说,Server端在执行操作的时候,可以回调(Callback)Client端的操作(这个操作时再Client端的Application Domain中执行的)。

现在我们来看如何一步一步实现在Remoting环境下的双向通信。在下面的Sample中,我们的逻辑是:调用一个数学计算的远程调用,除了传递相应的操作数之外,我们还传递一个对象,这个对象可以在Server端中回调 (Callback) 把运算结果在Client端显示出来。可以通过下面的URL下载源代码:http://www.cnblogs.com/files/artech/Artech.DuplexRemoting.zip
步骤一、构建整个Solution的整体构架

  • Artech.DuplexRemoting.Contract:Class Library Project,定义远程对象(Remote Object)和Callback对象的Contract(Interface)。实际上,站在Server端的角度上看,Callback的操作是在Client端的Application Domain中执行的,所以从本质上讲, Callback对象是Server端的远程对象。之所以定义这样一个Contract Project,其目的主要有以下两点:

如果没有把远程对象的Interface,对已某一个需要调用这个远程对象的Client来说,它必须引用远程对象本身。从安全的角度考虑,Server向Client过多暴露了操作的实现逻辑。如果我们把远程操作的Contract提取出来,Client只要引用这个Interface就可以了。

一般来说,远程对象的Contract相对时静态的(static),而业务逻辑的实现则是经常 变化的。因为Client只需要了解的是远程对象的Contract,所在无论Server端对远程对象的实现作了多大的变动,对不回对Client产生任何影响。

  • Artech.DuplexRemoting.Remoting:Class Library Project,定义远程对象本身。由于远程对象必须实现上边定义的Contract。所以需要引用Artech.DuplexRemoting.Contract。
  • Artech.DuplexRemoting.Hosting:Console Application Project,以Self-Host的方式Host Remoting。引用Artech.DuplexRemoting.Remoting。
  • Artech.DuplexRemoting.Client:Console Application Project,引用Artech.DuplexRemoting.Contract。

步骤二、定义Contract

IDuplexCalculator

   1: public interface IDuplexCalculator
   2: {
   3:     void Add(double x, double y, ICalculatorCallback callback);
   4: }

ICalculatorCallback

   1: public interface ICalculatorCallback
   2: {
   3:       void ShowResult(double x, double y, double result);
   4: }

步骤三、定义远程对象类型

DuplexCalculatorRemoting

   1: public class DuplexCalculatorRemoting : MarshalByRefObject, IDuplexCalculator
   2: {
   3:     public void Add(double x, double y, ICalculatorCallback callback)
   4:     {
   5:         Console.WriteLine("Invoke the method Add({0},{1}).", x, y);
   6:         double result = x + y;
   7:         callback.ShowResult(x, y, result);
   8:     }
   9: }

步骤四、Host远程对象

App.config

   1: <configuration>
   2:     <system.runtime.remoting>
   3:         <application name="Calculator">
   4:             <service>            
   5:                 <wellknown mode="SingleCall"
   6:                            type="Artech.DuplexRemoting.Remoting.DuplexCalculatorRemoting,Artech.DuplexRemoting.Remoting"
   7:                            objectUri="DuplexCalculator.soap" />
   8:             </service>
   9:  
  10:             <channels>
  11:                 <channel ref="http" port="8080">
  12:                     <serverProviders>
  13:                         <provider ref="wsdl" />
  14:                         <formatter ref="binary" typeFilterLevel="Full" />
  15:                     </serverProviders>
  16:                     <clientProviders>
  17:                         <formatter ref="binary" />
  18:                     </clientProviders>
  19:                 </channel>
  20:             </channels>
  21:         </application>
  22:     </system.runtime.remoting>
  23: </configuration>

Program.cs

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         System.Runtime.Remoting.RemotingConfiguration.Configure("Artech.DuplexRemoting.Hosting.exe.config",false);
   6:         Console.WriteLine("Calculator service has begun to listen ");
   7:         Console.Read();
   8:     }
   9: }

这里需要特别注意的有以下两点:

  • 在定义Channel是需要指定一个双向Channel(Bi-Directional Channel)。系统给我们定义一一系列的System-Defined Channel用于调用远程对象。其中有一些只能提供单向的通信——比如只支持Client到Server的通信,而另一些可以提供双向的通信——比如TCP Channel 和Http Channel.
  • 在ServerProvider Section,我们必须设置typeFilterLevel为Full。出于安全的考量,Remoting提供了两个反序列化级别(Level)——Low & Full。Low是默认的,如果把typeFilterLevel设为Low,Remoting之会反序列化Remoting基本功能相关的对象。而设为Full则意味着 Remoting 会反序列化所有类型。如果你想知道那些类型是在 Low Level 下被限制,请参考 http://msdn2.microsoft.com/en-us/library/5dxse167.aspx

之所以要把typeFilterLevelFull,是因为我们的远程调用里包含一Callback对象,它实际上是一个继承System.MarshalByRefObject类对象(这个的对象将在Artech.DuplexRemoting.Client中定义)。而这个对象是不会再Low Level下被自动反序列化。

   1: <channels>
   2:   <channel ref="http" port="8080">
   3:     <serverProviders>
   4:       <provider ref="wsdl" />
   5:       <formatter ref="binary" typeFilterLevel="Full" />
   6:     </serverProviders>
   7:     <clientProviders>
   8:       <formatter ref="binary" />
   9:     </clientProviders>
  10:   </channel>
  11: </channels>

   1: public interface IDuplexCalculator
   2: {
   3:      void Add(double x, double y, ICalculatorCallback callback);
   4: }

步骤五、定义Callback对象类型和调用远程对象

CalculatorCallbackHandler

   1: public class CalculatorCallbackHandler : MarshalByRefObject, ICalculatorCallback
   2: {
   3:     public void ShowResult(double x, double y, double result)
   4:     {
   5:         Console.WriteLine("x + y = {2} where x = {0} and y = {1}", x, y, result);
   6:     }
   7: }

App.config

   1: <configuration>
   2: <system.runtime.remoting>
   3:     <application>
   4:         <channels>
   5:             <channel ref="http" port="0">
   6:                 <clientProviders>
   7:                     <formatter ref="binary" />
   8:                 </clientProviders>
   9:                 <serverProviders>
  10:                     <formatter ref="binary" typeFilterLevel="Full" />
  11:                 </serverProviders>
  12:             </channel>
  13:         </channels>        
  14:     </application>
  15: </system.runtime.remoting>
  16: </configuration>

Program.cs

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         System.Runtime.Remoting.RemotingConfiguration.Configure("Artech.DuplexRemoting.Client.exe.config", false);
   6:  
   7:         InvocateDuplexCalculator("http://localhost:8080/Calculator/DuplexCalculator.soap");
   8:     }
   9:  
  10:     static void InvocateDuplexCalculator(string remoteAddress)
  11:     {
  12:         IDuplexCalculator proxy = (IDuplexCalculator)Activator.GetObject(typeof(IDuplexCalculator), remoteAddress);
  13:         proxy.Add(1, 2, new CalculatorCallbackHandler());
  14:         Console.Read();
  15:     }
  16: }

这里有两点需特别注意的:

  • 由于Server端时跨Application Domain远程地调用运行Client Application Domain中的Callback对象(Callback的执行实际是在Client而不在Server),所以Callback对象应该是一个MarshalByRefObject对象;
  • 上面我们以经提及,对于 Server 端了来说 Callback 对象实际上是一个远程对象(在 Callback 过程中 Client 端转变成 Server 端,而 Server 端转变成 Client 端)。 Server 端需要注册一些 Channel 用于 Client 访问寄宿在 Server 端的远程对象,同理, Server 需要 Callback 一个寄宿在 Client Application Domain 中的 Callback 对象, Client 端需要注册相应的 Channel;
  • Server 端一样,我们必须设置 typeFilterLevel Full  

到现在为止我们已经完成了所有的Program,我们来运行一下,在客户端你将得到如下的输出:

   1: x + y = 3 where x = 1 and y = 2

步骤六、将远程对象Host到IIS中

我们知道,Remoting有两种Host方式Self HostIIS Host,上面我们把Remoting Host到一个Console Application中; 现在我们把试着把它HostIIS中。实际上我们要做的工作很简单。

  • IIS Manager中添加一个虚拟目录对应Artech.DuplexRemoting.Remoting文件夹, 假设此虚拟目录的AliasArtech.DuplexRemoting

  • Artech.DuplexRemoting.Remoting根目录下中(也就是在http://localhost/Artech.DuplexRemoting根目录下)添加一个Web.config,并添加类似于Artech.DuplexRemoting.Hosting/App.Config Remoting Configuration

   1: <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
   2:   <system.runtime.remoting>
   3:     <application>
   4:       <service>
   5:         <wellknown mode="SingleCall"
   6:                    type="Artech.DuplexRemoting.Remoting.DuplexCalculatorRemoting,Artech.DuplexRemoting.Remoting"
   7:                    objectUri="DuplexCalculator.soap" />
   8:       </service>
   9:       <channels>
  10:         <channel ref="http">
  11:           <serverProviders>
  12:             <provider ref="wsdl" />
  13:             <formatter ref="binary" typeFilterLevel="Full" />
  14:           </serverProviders>
  15:           <clientProviders>
  16:             <formatter ref="binary" />
  17:           </clientProviders>
  18:         </channel>
  19:       </channels>
  20:     </application>
  21:   </system.runtime.remoting>
  22: </configuration>

这样我们可以不需要Hosting,就可以运行Client了。

我所理解的.NET Remoting:
我所理解的Remoting(1):Marshaling & Activation - Part I
我所理解的Remoting(1):Marshaling & Activation - Part II
我所理解的Remoting(2):远程对象生命周期的管理—Part I
我所理解的Remoting(2):远程对象的生命周期管理-Part II
我所理解的Remoting(3
):创建CAO Service Factory使接口和实现相互分离


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
3月前
|
Java Maven
【Netty 网络通信】启动通信服务端
【1月更文挑战第9天】【Netty 网络通信】启动通信服务端
|
3月前
|
网络协议
【Netty 网络通信】Socket 通信原理
【1月更文挑战第9天】【Netty 网络通信】Socket 通信原理
|
8月前
|
JSON 移动开发 网络协议
Web | 什么是 RPC 消息协议 ?
Web | 什么是 RPC 消息协议 ?
126 0
|
网络协议
通信模型与通信中的问题
通信模型 通信 信源 信源编码 信道 信宿 信源编码
169 0
通信模型与通信中的问题
|
人工智能 安全 网络协议
高质量通信gRPC入门,有了它,谁还用Socket
高质量通信gRPC入门,有了它,谁还用Socket
1116 0
高质量通信gRPC入门,有了它,谁还用Socket
|
网络协议 数据安全/隐私保护
RPC通信原理
RPC通信原理
316 0
RPC通信原理
|
JavaScript
|
网络协议
wcf双工通讯
首先说一个服务引用不成功的解决办法: 需要把服务端配置文件中的Binding换成: 或: 下面是一个wcf的简单示例: 服务契约代码: using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; namespace WcfService { // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IUser”。
956 0