WCF和Remoting之間的消息傳輸

一.NET Remoting 介紹

簡介

.NET Remoting與MSMQ不同,它不支持離線可得,另外隻適合.NET平臺的程序進行通信。它提供瞭一種允許對象通過應用程序域與另一個對象進行交互的框架。.NET 應用程序都在一個主應用程序域中執行的,在一個應用程序域中的代碼不能訪問另一個應用程序域的數據,然而在某些情況下,我們需要跨應用程序域,與另外的應用程序域進行通信,這時候就可以采用.NET Remoting技術來實現與另一個程序域中的對象進行交互。

基本原理

.NET Remoting技術是通過通道來實現兩個應用程序之間對象的通信的。

首先,客戶端通過Remoting技術的訪問通道來獲得服務器端對象,再通過代理解析為客戶端對象,也稱作透明代理,此時獲得客戶端對象隻是服務器對象的一個引用。這既保證瞭客戶端和服務端有關對象的松散耦合,同時優化瞭通信的性能。在這個過程中,當客戶端通過透明代理來調用遠程對象的方法時,此時會將調用封裝到一個消息對象中,該消息對象包括遠程對象信息,被調用的方法名和參數,然後透明代理會將調用委托給真實代理(RealProxy對象)的Invoke方法來生成一個IMethodCallMessage,

接著通過序列化把這個消息對象序列化成數據流發送到通道,通道會把數據流傳送到服務器端。當服務器接收到經過格式化的數據之後,首先從中通過反序列化來還原消息對象,之後在服務器端來激活遠程對象,並調用對應的方法,而方法的返回結果過程則是按照之前的方法反向重復一遍。具體的實現原理圖如下所示:

遠程對象:

是運行在服務器端的對象,客戶端不能直接調用,由於.NET Remoting傳遞的對象是以引用的方式,因此所傳遞的遠程對象必須繼承MarshalByRefObject類,這個類可以使遠程對象在.NET Remoting應用通信中使用,支持對象的跨域邊界訪問。

遠程對象的激活方式

在訪問服務器端的一個對象實例之前,必須通過一個名為Activation的進程創建它並進行初始化。這種客戶端通過通道來創建遠程對象的方式稱為對象的激活。在.NET Remoting中,遠程對象的激活分為兩大類:服務器端激活和客戶端激活。

1、服務器端激活(WellKnow(知名對象)激活模式)

為什麼稱為知名對象激活模式呢?是因為服務應用程序在激活對象實例之前會在一個眾所周知的統一資源標示符(URI)上發佈這個類型,然後該服務器進行會為此類型配置一個WellKnow對象,並根據指定的端口或地址來發佈對象。

.NET Remoting把服務器端激活又分為SingleTon模式和SingleCall模式兩種。

  • SingleTon模式:此為有狀態模式。.NET Remoting將為所有客戶端建立同一個對象實例。服務端隻在對象第一次被調用時創建服務對象,當對象處於活動狀態時,SingleTon實例會處理所有後來的客戶端訪問請求,而不管它們是同一個客戶端,還是其他客戶端。SingleTon實例將在方法調用中一直維護其狀態,類似static成員的概念。(相當於Application狀態)
  • SingleCall模式:是一種無狀態模式。則當客戶端調用遠程對象的方法時,Remoting會為每一個客戶端建立一個遠程對象實例,對象實例的銷毀則是由GC自動管理。類似實例成員的概念。(相當於Session狀態)

2、客戶端激活:

與Wellknow模式不同,。NET Remoting在激活每個對象實例的時候,會給每個客戶端激活的類型指派一個URI。客戶端激活模式一旦獲得客戶端的請求,將為每一個客戶端都建立一個實例引用。SingleCall模式與客戶端激活模式的區別有:

首先,對象實例創建的時間不同。客戶端激活方式是客戶一旦發出調用請求就實例化,而SingleCall則要等到調用對象方法時再創建。

其次,SingleCall模式激活的對象是無狀態的,對象聲明周期由GC管理,而客戶端激活的對象是有狀態的,其生命周期可自定義。

第三,兩種激活模式在服務器端和客戶端實現的方法不一樣,尤其是在客戶端,SingleCall模式由GetObject()來激活,它調用對象默認的構造函數,而客戶端激活模式,則通過CreateInstance()來激活,它可以傳遞參數,所以可以調用自定義的構造函數來創建實例。

通道:

在.NET Remoting中時通過通道來實現兩個應用程序域之間對象的通信。.NET Remoting中包括4中通道類型:

  • TcpChannel:Tcp通道使用Tcp協議來跨越.Net Remoting邊界來傳輸序列化的消息流,TcpChannel默認使用二進制格式序列化消息對象,因此具有更高的傳輸性能,但不提供任何內置的安全功能。
  • HttpChannel:Http通道使用Http協議在客戶端和服務器之間發生消息,使其在Internet上穿越防火墻來傳輸序列化的消息流。默認情況下,HttpChannel使用Soap格式序列化消息對象,因此它具有更好的互操作性,並且可以使用Http協議中的加密機制來對消息進行加密來保證安全性。因此,通常在局域網內,我們更多地使用TcpChannel,如果要穿越防火墻,則使用HttpChannel。
  • IpcChannel:進程間通信,隻使用同一個系統進程之間的通信,不需要主機名和端口號。而使用Http通道和Tcp通道都要指定主機名和端口號。
  • 自定義通道:自定義的傳輸通道可以使用任何基本的傳輸協議來進行通信,如UDP協議、SMTP協議等。

二、服務端激活方式:SAO

1、創建一個共享接口的dll,服務端和客戶端都要引用它。

//定義接口類ITax
 //編譯生成ITaxTemoting.dll
 //服務器端和客戶端都要添加該類dll的引用
 public interface ITax
 {
 double GetTax(int salary);
}

創建遠程對象,該對象必須繼承MarshalByRefObject對象。遠程對象類Tax繼承瞭基類MarshalByRefObject和接口ITax。

//定義遠程對象,必須繼承自MarshalByRefObject
//編譯生成TaxRemoting.dll,服務器端必須添加該dll的引用
public class Tax : MarshalByRefObject, ITax
{
    private int _callOCunt = 0;
    public Tax()
    {
        Console.WriteLine("Remoting object Tax 已激活");
    }

    //根據稅
    public double GetTax(int salary)
    {
        _callOCunt++;

        return (double)tax;
    }
}

2、服務端

需要添加System.Runtime.Remoting.dll引用

定義通道並監聽,註冊遠程對象。信道用於.NET客戶端和服務器端的通信。

在.NET Remoting中,是允許同時創建多個通道的,但是.NET Remoting要求通道的名字必須不同,因為名字是用來標識通道的唯一標識符。

TcpChannel channel = new TcpChannel(8085);//定義通道,還有HttpChannel\IPCChannel等
ChannelServices.RegisterChannel(channel, false);//註冊通道
RemotingConfiguration.RegisterWellKnownServiceType(typeof(Tax), "Tax1", WellKnownObjectMode.SingleCall);
//在服務端註冊一個知名的遠程對象Tax,ObjectURI為Tax,知名對象模式為單次調用方式。

3、客戶端

註冊信道,根據URL獲取遠程對象代理,使用代理調用服務器端的遠程對象。

void Main()
{
    TcpChannel channel = new TcpChannel();
    ChannelServices.RegisterChannel(channel, false);
    ITax obj = (ITax)Activator.GetObject(typeof(ITax), "tcp://localhost:8085/Tax1");//根據URL獲取遠程對象代理,使用此代理調用服務端的遠程對象。
    if (obj == null)
    {
        Console.WriteLine("Could not locate TCP server");
    }
    Console.WriteLine(obj.GetTax(1).ToString());//調用遠程對象的方法獲取結果,此時調用服務端的類的默認構造函數實例化。
    Console.WriteLine(obj.GetCallCount().ToString());

    Console.WriteLine(obj.GetTax(1).ToString());
    Console.WriteLine(obj.GetCallCount().ToString());
}
//也可以先為客戶端註冊類型,再實例化對象(不推薦)
RemotingConfiguration.RegisterWellKnownClientType(typeof(ITax), "tcp://localhost:8085/Tax1");
Tax obj = new Tax();
obj.GetTax();

三、客戶端激活方式:CAO

可以調用服務端非默認的帶參數的構造函數,服務端為每個客戶端保存不同的狀態。

1、聲明定義一個公共類

public class Tax : MarshalByRefObject, ITax
{
//代碼同服務端SAO
}

2、服務端,應用Tax所在的程序集

TcpChannel channel = new TcpChannel(8085);
ChannelServices.RegisterChannel(channel, false);
RemotingConfiguration.ApplicationName = "Tax1";
RemotingConfiguration.RegisterActivatedServiceType(typeof(Tax));

3、客戶端

可以使用Soapsuds.exe分離Tax程序集共客戶端調用(即不包含具體的實現內容)

E:>soapsuds -url:http://127.0.0.1:8502/TaxTax1?wsdl -oa:ClientProxy.dll

這將為我們在E盤的根目錄下生成ClientProxy.dll文件,這個文件將用於客戶端成生代理。

void Main()
{
    TcpChannel channel = new TcpChannel();
    ChannelServices.RegisterChannel(channel, false);
    Tax obj = (Tax)Activator.CreateInstance(typeof(ITax), null, new[] {new UrlAttribute ("tcp://localhost:8085/Tax1")});//根據URL獲取遠程對象代理,使用此代理調用服務端的遠程對象。

    Console.WriteLine(obj.GetTax(1).ToString());
    Console.WriteLine(obj.GetCallCount().ToString());

    Console.WriteLine(obj.GetTax(1).ToString());
    Console.WriteLine(obj.GetCallCount().ToString());
}

//也可以先為客戶端註冊類型,再實例化對象(不推薦)
RemotingConfiguration.RegisterActivatedClientType(typeof(ITax), "tcp://localhost:8085/Tax1");//Activator.GetObject

Tax obj = new Tax();
obj.GetTax();

4、關閉註銷通道

channel.StartListening(null);
ChannelServices.UnregisterChannel(channel);

四、使用配置文件來重寫上面的分佈式程序

服務端:

RemotingConfiguration.Configure("RemotingServerHostByConfig.exe.config", false);

服務端的配置文件app.config的內容為

<configuration>
 <system.runtime.remoting>
 <application>
   <service> <!--服務端,如果是客戶端改成Client –>
     <wellknown type="Tax" objectUri="Tax1" mode="SingleCall" />  <!--客戶端激活改成activator—>
   </service>
   <channels>
      <channel ref="tcp" port="8085" />
   </channels>
 </application>
</configuration>

客戶端:

RemotingConfiguration.Configure("RemotingClientByConfig.exe.config", false);

客戶端配置文件的內容為:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.runtime.remoting>
    <application>
      <client>
        <wellknown  type="Tax"  url="tcp://localhost:8085/Tax1" />
      </client>
      <channels>    
        <channel ref="tcp" port="8085"></channel>
      </channels>
    </application>
  </system.runtime.remoting>
</configuration>

以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持LevelAH。

推薦閱讀: