天天看點

delphi prism 如何通過.net 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的調用方式。

       這次不是講用C#如果實作,而是用Delphi prism 實作。

PRINCE.Contract:Class Library Project,定義遠端對象(Remote Object)和Callback對象的Contract(Interface)。實際上,站在Server端的角度上看,Callback的操作是在Client端的Application Domain中執行的,是以從本質上講, Callback對象是Server端的遠端對象。

之是以定義這樣一個Contract Project,其目的主要有以下幾點:

1.         如果沒有把遠端對象的Interface,對已某一個需要調用這個遠端對象的Client來說,它必須引用遠端對象本身。從安全的角度考慮,Server向Client過多暴露了操作的實作邏輯。如果我們把遠端操作的Contract提取出來,Client隻要引用這個Interface就可以了。

2.         一般來說,遠端對象的Contract相對時靜态的(static),而業務邏輯的實作則是經常 變化的。因為Client隻需要了解的是遠端對象的Contract,所在無論Server端對遠端對象的實作作了多大的變動,對不回對Client産生任何影響。

PRINCE.Service:Class Library Project,定義遠端對象本身。由于遠端對象必須實作上邊定義的Contract。是以需要引用PRINCE.Contract。

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

PRINCE.Client:Console Application Project,引用PRINCE.Contract。

delphi prism 如何通過.net Remoting實作雙向通信

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是需要指定一個雙向Channel(Bi-Directional Channel)。系統給我們定義一一系列的System-Defined Channel用于調用遠端對象。其中有一些隻能提供單向的通信——比如隻支援Client到Server的通信,而另一些可以提供雙向的通信——比如TCP Channel 和Http Channel.

2.         在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。

之是以要把typeFilterLevel為Full,是因為我們的遠端調用裡包含一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一個寄宿在Client端Application Domain中的Callback對象,Client端需要注冊相應的Channel

    3.         和Server端一樣,我們必須設定typeFilterLevel為Full。

到現在為止我們已經完成了所有的Program,我們來運作一下。

delphi prism 如何通過.net Remoting實作雙向通信

繼續閱讀