delphi prism 如何通过.net Remoting实现双向通信

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

       这次不是讲用C#如果实现,而是用Delphi prism 实现。

 

PRINCE.ContractClass Library Project,定义远程对象(Remote Object)和Callback对象的ContractInterface)。实际上,站在Server端的角度上看,Callback的操作是在Client端的Application Domain中执行的,所以从本质上讲, Callback对象是Server端的远程对象。

之所以定义这样一个Contract Project,其目的主要有以下几点:

 

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

 

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

 

PRINCE.ServiceClass Library Project,定义远程对象本身。由于远程对象必须实现上边定义的Contract。所以需要引用PRINCE.Contract

PRINCE.HostingConsole Application Project,以Self-Host的方式Host Remoting。引用PRINCE.Service, PRINCE.Contract

PRINCE.ClientConsole Application Project,引用PRINCE.Contract

 

 

IDuplexCalculator.pas

 

namespace PRINCE.Contract;

 

interface

 

uses

  System.Collections.Generic,

  System.Linq,

  System.Text;

 

type

  IDuplexCalculator = public interface 

      procedure &Add(x,y:double;callback:ICalculatorCallback);

  end;

 

implementation

 

end.

 

ICalculatorCallback.pas

 

namespace PRINCE.Contract;

 

interface

 

type

  ICalculatorCallback = public interface 

      procedure ShowResult(x,y,resulted:Double );

  end;

 

implementation

 

end.

 

Step 3 PRINCE.Service定义远程对象

 

DuplexCalculatorRemoting.pas

 

namespace PRINCE.Service;

 

interface

 

uses PRINCE.Contract;

 

type

  DuplexCalculatorRemoting = public class(MarshalByRefObject, IDuplexCalculator)

  private

  protected

  public

       procedure &Add(x,y:double;callback:ICalculatorCallback);

  end;

 

implementation

 

 

procedure DuplexCalculatorRemoting.&Add(x, y: Double; callback:ICalculatorCallback);

var

    resulted:Double;

begin

   Console.WriteLine('Invoke the method Add(' + x.ToString() + ',' + y.ToString() +')');

   resulted := x + y;           

   callback.ShowResult(x,y,resulted);

end;

 

end.

 

Step 4 PRINCE.Hosting Host远程对象

 

App.config

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<system.runtime.remoting>

  <application name="Calculator">

    <service>

      <wellknown mode="SingleCall"

             type="PRINCE.Service.DuplexCalculatorRemoting,PRINCE.Service"

             objectUri="DuplexCalculator.soap" />

    </service>

 

    <channels>

      <channel ref="http" port="8080">

        <serverProviders>

          <provider ref="wsdl" />

          <formatter ref="binary" typeFilterLevel="Full" />

        </serverProviders>

        <clientProviders>

          <formatter ref="binary" />

        </clientProviders>

      </channel>

    </channels>

 

    <channels>

      <channel ref="tcp" port="8081">

        <serverProviders>

          <provider ref="wsdl" />

          <formatter ref="binary" typeFilterLevel="Full" />

        </serverProviders>

        <clientProviders>

          <formatter ref="binary" />

        </clientProviders>

      </channel>

    </channels>

  </application>

</system.runtime.remoting>

</configuration>

 

 

Program.pas

 

 

namespace Hosting;

 

interface

 

uses

  System.Linq;

 

 

type

  ConsoleApp = class

  public

    class method Main;

  end;

 

implementation

 

class method ConsoleApp.Main;

begin

  System.Runtime.Remoting.RemotingConfiguration.Configure(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,false);

  Console.WriteLine("Calculator service has begun to listen... ...");

  Console.Read();

 

end;

 

end

 

.

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

 

1         在定义Channel是需要指定一个双向ChannelBi-Directional Channel)。系统给我们定义一一系列的System-Defined Channel用于调用远程对象。其中有一些只能提供单向的通信——比如只支持ClientServer的通信,而另一些可以提供双向的通信——比如TCP Channel Http Channel.

 

2         ServerProvider Section,我们必须设置typeFilterLevelFull。出于安全的考量,Remoting提供了两个反序列化级别(Level)——Low & FullLow是默认的,如果把typeFilterLevel设为LowRemoting之会反序列化Remoting基本功能相关的对象。而设为Full则意味着Remoting会反序列化所有类型。如果你想知道那些类型是在Low Level下被限制,请参考http://msdn2.microsoft.com/en-us/library/5dxse167.aspx

 

 

 

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

 

 

<channels>

                <channel ref="http" port="8080">

                    <serverProviders>

                        <provider ref="wsdl" />

                        <formatter ref="binary" typeFilterLevel="Full" />

                    </serverProviders>

                    <clientProviders>

                        <formatter ref="binary" />

                    </clientProviders>

                </channel>

            </channels>

 

 

    public interface IDuplexCalculator

    {

        void Add(double x, double y, ICalculatorCallback callback);

    }

 

 

Step 4 PRINCE.Client定义Callback对象和调用远程对象

 

CalculatorCallbackHandler.pas

 

namespace PRINCE.Client;

 

interface

 

uses

  System.Collections.Generic,

  System.Linq,

  System.Text,

  PRINCE.Contract;

 

type

  CalculatorCallbackHandler = public class(MarshalByRefObject, ICalculatorCallback)

  private

  protected

  public

      procedure ShowResult(x,y,resulted:Double );

  end;

 

implementation

 

procedure CalculatorCallbackHandler.ShowResult(x, y, resulted: Double);

begin

    Console.WriteLine('x + y = ' + resulted.ToString() + ' where x = ' + x.ToString() + ' and y = ' + y.ToString());

end;

 

end.

 

App.config

 

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.runtime.remoting>

    <application>

      <channels>

        <channel ref="http" port="0">

          <clientProviders>

            <formatter ref="binary" />

          </clientProviders>

          <serverProviders>

            <formatter ref="binary" typeFilterLevel="Full" />

          </serverProviders>

        </channel>

      </channels>

    </application>

  </system.runtime.remoting>

</configuration>

 

 

Program.pas

 

 

namespace PRINCE.Client;

 

interface

 

uses

  System.Linq,

  PRINCE.Contract;

 

 

type

  ConsoleApp = class

  private

      class method InvocateDuplexCalculator(remoteAddress:String);

  public

    class method Main;

  end;

 

implementation

 

class method ConsoleApp.Main;

begin

   System.Runtime.Remoting.RemotingConfiguration.Configure(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, false);

   InvocateDuplexCalculator("http://localhost:8080/Calculator/DuplexCalculator.soap");      

end;

 

class method ConsoleApp.InvocateDuplexCalculator(remoteAddress: String);

var

    proxy:IDuplexCalculator;

begin

    proxy := IDuplexCalculator(Activator.GetObject(typeof(IDuplexCalculator), remoteAddress));

    proxy.Add(1,2,new CalculatorCallbackHandler());

    Console.Read();

end;

 

end.

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

   1.    由于Server端时跨Application Domain远程地调用运行Client Application Domain中的Callback对象(Callback的执行实际是在Client而不在Server),所以Callback对象应该是一个MarshalByRefObject对象。

   2         上面我们以经提及,对于Server端了来说Callback对象实际上是一个远程对象(在Callback过程中Client端转变成Server端,而Server端转变成Client端)。Server端需要注册一些Channel用于Client访问寄宿在Server端的远程对象,同理,Server需要Callback一个寄宿在ClientApplication Domain中的Callback对象,Client端需要注册相应的Channel

    3         Server端一样,我们必须设置typeFilterLevelFull

 

 

 

 

到现在为止我们已经完成了所有的Program,我们来运行一下。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值