佳木斯湛栽影视文化发展公司

主頁 > 知識庫 > Microsoft .Net Remoting系列教程之一:.Net Remoting基礎(chǔ)篇

Microsoft .Net Remoting系列教程之一:.Net Remoting基礎(chǔ)篇

熱門標簽:地方門戶網(wǎng)站 電子圍欄 智能手機 硅谷的囚徒呼叫中心 蘋果 呼叫中心 解決方案 服務器配置

一、Remoting基礎(chǔ)

  什么是Remoting,簡而言之,我們可以將其看作是一種分布式處理方式。從微軟的產(chǎn)品角度來看,可以說Remoting就是DCOM的一種升級,它改善了很多功能,并極好的融合到.Net平臺下。Microsoft .NET Remoting 提供了一種允許對象通過應用程序域與另一對象進行交互的框架。這也正是我們使用Remoting的原因。為什么呢?在Windows操作系統(tǒng)中,是將應用程序分離為單獨的進程。這個進程形成了應用程序代碼和數(shù)據(jù)周圍的一道邊界。如果不采用進程間通信(RPC)機制,則在一個進程中執(zhí)行的代碼就不能訪問另一進程。這是一種操作系統(tǒng)對應用程序的保護機制。然而在某些情況下,我們需要跨過應用程序域,與另外的應用程序域進行通信,即穿越邊界。

  在Remoting中是通過通道(channel)來實現(xiàn)兩個應用程序域之間對象的通信的。如圖所示:

  首先,客戶端通過Remoting,訪問通道以獲得服務端對象,再通過代理解析為客戶端對象。這就提供一種可能性,即以服務的方式來發(fā)布服務器對象。遠程對象代碼可以運行在服務器上(如服務器激活的對象和客戶端激活的對象),然后客戶端再通過Remoting連接服務器,獲得該服務對象并通過序列化在客戶端運行。

  在Remoting中,對于要傳遞的對象,設(shè)計者除了需要了解通道的類型和端口號之外,無需再了解數(shù)據(jù)包的格式。但必須注意的是,客戶端在獲取服務器端對象時,并不是獲得實際的服務端對象,而是獲得它的引用。這既保證了客戶端和服務器端有關(guān)對象的松散耦合,同時也優(yōu)化了通信的性能。

1、Remoting的兩種通道

  Remoting的通道主要有兩種:Tcp和Http。在.Net中,System.Runtime.Remoting.Channel中定義了IChannel接口。IChannel接口包括了TcpChannel通道類型和Http通道類型。它們分別對應Remoting通道的這兩種類型。

  TcpChannel類型放在名字空間System.Runtime.Remoting.Channel.Tcp中。Tcp通道提供了基于Socket的傳輸工具,使用Tcp協(xié)議來跨越Remoting邊界傳輸序列化的消息流。TcpChannel類型默認使用二進制格式序列化消息對象,因此它具有更高的傳輸性能。HttpChannel類型放在名字空間System.Runtime.Remoting.Channel.Http中。它提供了一種使用Http協(xié)議,使其能在Internet上穿越防火墻傳輸序列化消息流。默認情況下,HttpChannel類型使用Soap格式序列化消息對象,因此它具有更好的互操作性。通常在局域網(wǎng)內(nèi),我們更多地使用TcpChannel;如果要穿越防火墻,則使用HttpChannel。

2、遠程對象的激活方式

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

  (1) 服務器端激活,又叫做WellKnow方式,很多又翻譯為知名對象。為什么稱為知名對象激活模式呢?是因為服務器應用程序在激活對象實例之前會在一個眾所周知的統(tǒng)一資源標識符(URI)上來發(fā)布這個類型。然后該服務器進程會為此類型配置一個WellKnown對象,并根據(jù)指定的端口或地址來發(fā)布對象。.Net Remoting把服務器端激活又分為SingleTon模式和SingleCall模式兩種。

  SingleTon模式:此為有狀態(tài)模式。如果設(shè)置為SingleTon激活方式,則Remoting將為所有客戶端建立同一個對象實例。當對象處于活動狀態(tài)時,SingleTon實例會處理所有后來的客戶端訪問請求,而不管它們是同一個客戶端,還是其他客戶端。SingleTon實例將在方法調(diào)用中一直維持其狀態(tài)。舉例來說,如果一個遠程對象有一個累加方法(i=0;++i),被多個客戶端(例如兩個)調(diào)用。如果設(shè)置為SingleTon方式,則第一個客戶獲得值為1,第二個客戶獲得值為2,因為他們獲得的對象實例是相同的。如果熟悉Asp.Net的狀態(tài)管理,我們可以認為它是一種Application狀態(tài)。

  SingleCall模式:SingleCall是一種無狀態(tài)模式。一旦設(shè)置為SingleCall模式,則當客戶端調(diào)用遠程對象的方法時,Remoting會為每一個客戶端建立一個遠程對象實例,至于對象實例的銷毀則是由GC自動管理的。同上一個例子而言,則訪問遠程對象的兩個客戶獲得的都是1。我們?nèi)匀豢梢越梃bAsp.Net的狀態(tài)管理,認為它是一種Session狀態(tài)。

  (2) 客戶端激活。與WellKnown模式不同,Remoting在激活每個對象實例的時候,會給每個客戶端激活的類型指派一個URI??蛻舳思せ钅J揭坏┇@得客戶端的請求,將為每一個客戶端都建立一個實例引用。SingleCall模式和客戶端激活模式是有區(qū)別的:首先,對象實例創(chuàng)建的時間不一樣??蛻舳思せ罘绞绞强蛻粢坏┌l(fā)出調(diào)用的請求,就實例化;而SingleCall則是要等到調(diào)用對象方法時再創(chuàng)建。其次,SingleCall模式激活的對象是無狀態(tài)的,對象生命期的管理是由GC管理的,而客戶端激活的對象則有狀態(tài),其生命周期可自定義。其三,兩種激活模式在服務器端和客戶端實現(xiàn)的方法不一樣。尤其是在客戶端,SingleCall模式是由GetObject()來激活,它調(diào)用對象默認的構(gòu)造函數(shù)。而客戶端激活模式,則通過CreateInstance()來激活,它可以傳遞參數(shù),所以可以調(diào)用自定義的構(gòu)造函數(shù)來創(chuàng)建實例。

二、遠程對象的定義

  前面講到,客戶端在獲取服務器端對象時,并不是獲得實際的服務端對象,而是獲得它的引用。因此在Remoting中,對于遠程對象有一些必須的定義規(guī)范要遵循。

  由于Remoting傳遞的對象是以引用的方式,因此所傳遞的遠程對象類必須繼承MarshalByRefObject。MSDN對MarshalByRefObject的說明是:MarshalByRefObject 是那些通過使用代理交換消息來跨越應用程序域邊界進行通信的對象的基類。不是從 MarshalByRefObject 繼承的對象會以隱式方式按值封送。當遠程應用程序引用一個按值封送的對象時,將跨越遠程處理邊界傳遞該對象的副本。因為您希望使用代理方法而不是副本方法進行通信,因此需要繼承MarshallByRefObject。

以下是一個遠程對象類的定義:

public class ServerObject:MarshalByRefObject
{
 public Person GetPersonInfo(string name,string sex,int age)
 {
 Person person = new Person();
 person.Name = name;
 person.Sex = sex;
 person.Age = age;
 return person;
 }
}

  這個類只實現(xiàn)了最簡單的方法,就是設(shè)置一個人的基本信息,并返回一個Person類對象。注意這里返回的Person類。由于這里所傳遞的Person則是以傳值的方式來完成的,而Remoting要求必須是引用的對象,所以必須將Person類序列化。

  因此,在Remoting中的遠程對象中,如果還要調(diào)用或傳遞某個對象,例如類,或者結(jié)構(gòu),則該類或結(jié)構(gòu)則必須實現(xiàn)串行化

Attribute[SerializableAttribute]:
[Serializable]
public class Person
{
 public Person()
 {
 
 }

 private string name;
 private string sex;
 private int age;

 public string Name
 {
 get {return name;}
 set {name = value;}
 }

 public string Sex
 {
 get {return sex;}
 set {sex = value;}
 }

 public int Age
 {
 get {return age;}
 set {age = value;}
 }
}

  將該遠程對象以類庫的方式編譯成Dll。這個Dll將分別放在服務器端和客戶端,以添加引用。

  在Remoting中能夠傳遞的遠程對象可以是各種類型,包括復雜的DataSet對象,只要它能夠被序列化。遠程對象也可以包含事件,但服務器端對于事件的處理比較特殊,我將在本系列之三中介紹。

三、服務器端

根據(jù)第一部分所述,根據(jù)激活模式的不同,通道類型的不同服務器端的實現(xiàn)方式也有所不同。大體上說,服務器端應分為三步:

1、注冊通道

  要跨越應用程序域進行通信,必須實現(xiàn)通道。如前所述,Remoting提供了IChannel接口,分別包含TcpChannel和HttpChannel兩種類型的通道。這兩種類型除了性能和序列化數(shù)據(jù)的格式不同外,實現(xiàn)的方式完全一致,因此下面我們就以TcpChannel為例。

  注冊TcpChannel,首先要在項目中添加引用“System.Runtime.Remoting”,然后using名字空間:System.Runtime.Remoting.Channel.Tcp。代碼如下:

TcpChannel channel = new TcpChannel(8080);
ChannelServices.RegisterChannel(channel);

在實例化通道對象時,將端口號作為參數(shù)傳遞。然后再調(diào)用靜態(tài)方法RegisterChannel()來注冊該通道對象即可。

2、注冊遠程對象

注冊了通道后,要能激活遠程對象,必須在通道中注冊該對象。根據(jù)激活模式的不同,注冊對象的方法也不同。

(1) SingleTon模式

對于WellKnown對象,可以通過靜態(tài)方法RemotingConfiguration.RegisterWellKnownServiceType()來實現(xiàn):

RemotingConfiguration.RegisterWellKnownServiceType(
 typeof(ServerRemoteObject.ServerObject),
 "ServiceMessage",WellKnownObjectMode.SingleTon);

(2)SingleCall模式

注冊對象的方法基本上和SingleTon模式相同,只需要將枚舉參數(shù)WellKnownObjectMode改為SingleCall就可以了。

RemotingConfiguration.RegisterWellKnownServiceType(
 typeof(ServerRemoteObject.ServerObject),
 "ServiceMessage",WellKnownObjectMode.SingleCall);

(3)客戶端激活模式

對于客戶端激活模式,使用的方法又有不同,但區(qū)別不大,看了代碼就一目了然。

RemotingConfiguration.ApplicationName = "ServiceMessage";
RemotingConfiguration.RegisterActivatedServiceType(
 typeof(ServerRemoteObject.ServerObject));

  為什么要在注冊對象方法前設(shè)置ApplicationName屬性呢?其實這個屬性就是該對象的URI。對于WellKnown模式,URI是放在RegisterWellKnownServiceType()方法的參數(shù)中,當然也可以拿出來專門對ApplicationName屬性賦值。而RegisterActivatedServiceType()方法的重載中,沒有ApplicationName的參數(shù),所以必須分開。

3、注銷通道

  如果要關(guān)閉Remoting的服務,則需要注銷通道,也可以關(guān)閉對通道的監(jiān)聽。在Remoting中當我們注冊通道的時候,就自動開啟了通道的監(jiān)聽。而如果關(guān)閉了對通道的監(jiān)聽,則該通道就無法接受客戶端的請求,但通道仍然存在,如果你想再一次注冊該通道,會拋出異常。

//獲得當前已注冊的通道;
IChannel[] channels = ChannelServices.RegisteredChannels;

//關(guān)閉指定名為MyTcp的通道;
foreach (IChannel eachChannel in channels)
{
 if (eachChannel.ChannelName == "MyTcp")
 {
 TcpChannel tcpChannel = (TcpChannel)eachChannel;

 //關(guān)閉監(jiān)聽;
 tcpChannel.StopListening(null);

 //注銷通道;
 ChannelServices.UnregisterChannel(tcpChannel);
 }
}

  代碼中,RegisterdChannel屬性獲得的是當前已注冊的通道。在Remoting中,是允許同時注冊多個通道的,這一點會在后面說明。

四、客戶端

  客戶端主要做兩件事,一是注冊通道。這一點從圖一就可以看出,Remoting中服務器端和客戶端都必須通過通道來傳遞消息,以獲得遠程對象。第二步則是獲得該遠程對象。

1、注冊通道:

TcpChannel channel = new TcpChannel();
ChannelServices.RegisterChannel(channel);

  注意在客戶端實例化通道時,是調(diào)用的默認構(gòu)造函數(shù),即沒有傳遞端口號。事實上,這個端口號是缺一不可的,只不過它的指定被放在后面作為了Uri的一部分。

2、獲得遠程對象。

  與服務器端相同,不同的激活模式?jīng)Q定了客戶端的實現(xiàn)方式也將不同。不過這個區(qū)別僅僅是WellKnown激活模式和客戶端激活模式之間的區(qū)別,而對于SingleTon和SingleCall模式,客戶端的實現(xiàn)完全相同。

(1) WellKnown激活模式

  要獲得服務器端的知名遠程對象,可通過Activator進程的GetObject()方法來獲得:

ServerRemoteObject.ServerObject serverObj = (ServerRemoteObject.ServerObject)Activator.GetObject(
 typeof(ServerRemoteObject.ServerObject), "tcp://localhost:8080/ServiceMessage");

  首先以WellKnown模式激活,客戶端獲得對象的方法是使用GetObject()。其中參數(shù)第一個是遠程對象的類型。第二個參數(shù)就是服務器端的uri。如果是http通道,自然是用http://localhost:8080/ServiceMessage了。因為我是用本地機,所以這里是localhost,你可以用具體的服務器IP地址來代替它。端口必須和服務器端的端口一致。后面則是服務器定義的遠程對象服務名,即ApplicationName屬性的內(nèi)容。

(2) 客戶端激活模式

  如前所述,WellKnown模式在客戶端創(chuàng)建對象時,只能調(diào)用默認的構(gòu)造函數(shù),上面的代碼就說明了這一點,因為GetObject()方法不能傳遞構(gòu)造函數(shù)的參數(shù)。而客戶端激活模式則可以通過自定義的構(gòu)造函數(shù)來創(chuàng)建遠程對象。

客戶端激活模式有兩種方法:
1) 調(diào)用RemotingConfiguration的靜態(tài)方法RegisterActivatedClientType()。這個方法返回值為Void,它只是將遠程對象注冊在客戶端而已。具體的實例化還需要調(diào)用對象類的構(gòu)造函數(shù)。

RemotingConfiguration.RegisterActivatedClientType(    
 typeof(ServerRemoteObject.ServerObject),
 "tcp://localhost:8080/ServiceMessage");
ServerRemoteObject.ServerObject serverObj = new ServerRemoteObject.ServerObject();

2) 調(diào)用進程Activator的CreateInstance()方法。這個方法將創(chuàng)建方法參數(shù)指定類型的類對象。它與前面的GetObject()不同的是,它要在客戶端調(diào)用構(gòu)造函數(shù),而GetObject()只是獲得對象,而創(chuàng)建實例是在服務器端完成的。CreateInstance()方法有很多個重載,我著重說一下其中常用的兩個。

a、 public static object CreateInstance(Type type, object[] args, object[] activationAttributes);

參數(shù)說明:
type:要創(chuàng)建的對象的類型。
args :與要調(diào)用構(gòu)造函數(shù)的參數(shù)數(shù)量、順序和類型匹配的參數(shù)數(shù)組。如果 args 為空數(shù)組或空引用(Visual Basic 中為 Nothing),則調(diào)用不帶任何參數(shù)的構(gòu)造函數(shù)(默認構(gòu)造函數(shù))。
activationAttributes :包含一個或多個可以參與激活的屬性的數(shù)組。

  這里的參數(shù)args是一個object[]數(shù)組類型。它可以傳遞要創(chuàng)建對象的構(gòu)造函數(shù)中的參數(shù)。從這里其實可以得到一個結(jié)論:WellKnown激活模式所傳遞的遠程對象類,只能使用默認的構(gòu)造函數(shù);而Activated模式則可以用戶自定義構(gòu)造函數(shù)。activationAttributes參數(shù)在這個方法中通常用來傳遞服務器的url。
假設(shè)我們的遠程對象類ServerObject有個構(gòu)造函數(shù):

ServerObject(string pName,string pSex,int pAge)
{
 name = pName;
 sex = pSex;
 age = pAge;
}

那么實現(xiàn)的代碼是:

object[] attrs = {new UrlAttribute("tcp://localhost:8080/ServiceMessage")};
object[] objs = new object[3];
objs[0] = "wayfarer";
objs[1] = "male";
objs[2] = 28;
ServerRemoteObject.ServerObject = Activator.CreateInstance(
 typeof(ServerRemoteObject.ServerObject),objs,attrs);

可以看到,objs[]數(shù)組傳遞的就是構(gòu)造函數(shù)的參數(shù)。

b、public static ObjectHandle CreateInstance(string assemblyName, string typeName, object[] activationAttribute);

參數(shù)說明:
assemblyName :將在其中查找名為 typeName 的類型的程序集的名稱。如果 assemblyName 為空引用(Visual Basic 中為 Nothing),則搜索正在執(zhí)行的程序集。
typeName:首選類型的名稱。
activationAttributes :包含一個或多個可以參與激活的屬性的數(shù)組。

參數(shù)說明一目了然。注意這個方法返回值為ObjectHandle類型,因此代碼與前不同:

object[] attrs = {new UrlAttribute("tcp://localhost:8080/EchoMessage")};   
ObjectHandle handle = Activator.CreateInstance("ServerRemoteObject",
 "ServerRemoteObject.ServerObject",attrs);
ServerRemoteObject.ServerObject obj = (ServerRemoteObject.ServerObject)handle.Unwrap();

這個方法實際上是調(diào)用的默認構(gòu)造函數(shù)。ObjectHandle.Unwrap()方法是返回被包裝的對象。

說明:要使用UrlAttribute,還需要在命名空間中添加:using System.Runtime.Remoting.Activation;

五、Remoting基礎(chǔ)的補充

  通過上面的描述,基本上已經(jīng)完成了一個最簡單的Remoting程序。這是一個標準的創(chuàng)建Remoting程序的方法,但在實際開發(fā)過程中,我們遇到的情況也許千奇百怪,如果只掌握一種所謂的“標準”,就妄想可以“一招鮮、吃遍天”,是不可能的。

1、注冊多個通道

  在Remoting中,允許同時創(chuàng)建多個通道,即根據(jù)不同的端口創(chuàng)建不同的通道。但是,Remoting要求通道的名字必須不同,因為它要用來作為通道的唯一標識符。雖然IChannel有ChannelName屬性,但這個屬性是只讀的。因此前面所述的創(chuàng)建通道的方法無法實現(xiàn)同時注冊多個通道的要求。

這個時候,我們必須用到System.Collection中的IDictionary接口:

注冊Tcp通道:

IDictionary tcpProp = new Hashtable();
tcpProp["name"] = "tcp9090";
tcpProp["port"] = 9090;
IChannel channel = new TcpChannel(tcpProp,
 new BinaryClientFormatterSinkProvider(),
 new BinaryServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel);

注冊Http通道:

IDictionary httpProp = new Hashtable();
httpProp["name"] = "http8080";
httpProp["port"] = 8080;
IChannel channel = new HttpChannel(httpProp,
 new SoapClientFormatterSinkProvider(),
 new SoapServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel);

在name屬性中,定義不同的通道名稱就可以了。

2、遠程對象元數(shù)據(jù)相關(guān)性

  由于服務器端和客戶端都要用到遠程對象,通常的方式是生成兩份完全相同的對象Dll,分別添加引用。不過為了代碼的安全性,且降低客戶端對遠程對象元數(shù)據(jù)的相關(guān)性,我們有必要對這種方式進行改動。即在服務器端實現(xiàn)遠程對象,而在客戶端則刪除這些實現(xiàn)的元數(shù)據(jù)。

由于激活模式的不同,在客戶端創(chuàng)建對象的方法也不同,所以要分離元數(shù)據(jù)的相關(guān)性,也應分為兩種情況。

(1) WellKnown激活模式:

通過接口來實現(xiàn)。在服務器端,提供接口和具體類的實現(xiàn),而在客戶端僅提供接口:

public interface IServerObject
{
 Person GetPersonInfo(string name,string sex,int age);
}

public class ServerObject:MarshalByRefObject,IServerObject
{ ......}

注意:兩邊生成該對象程序集的名字必須相同,嚴格地說,是命名空間的名字必須相同。
          
(2) 客戶端激活模式:

  如前所述,對于客戶端激活模式,不管是使用靜態(tài)方法,還是使用CreateInstance()方法,都必須在客戶端調(diào)用構(gòu)造函數(shù)實例化對象。所以,在客戶端我們提供的遠程對象,就不能只提供接口,而沒有類的實現(xiàn)。實際上,要做到與遠程對象元數(shù)據(jù)的分離,可以由兩種方法供選擇:

a、利用WellKnown激活模式模擬客戶端激活模式:

方法是利用設(shè)計模式中的“抽象工廠”,下面的類圖表描述了總體解決方案:

我們在服務器端的遠程對象中加上抽象工廠的接口和實現(xiàn)類:

public interface IServerObject
{
 Person GetPersonInfo(string name,string sex,int age);
}

public interface IServerObjFactory
{
 IServerObject CreateInstance();  
}

public class ServerObject:MarshalByRefObject,IServerObject
{
 public Person GetPersonInfo(string name,string sex,int age)
 {
  Person person = new Person();
  person.Name = name;
  person.Sex = sex;
  person.Age = age;
  return person;
 }  
}

public class ServerObjFactory:MarshalByRefObject,IServerObjFactory
{
 public IServerObject CreateInstance()
 {
  return new ServerObject();
 }
}

然后再客戶端的遠程對象中只提供工廠接口和原來的對象接口:

public interface IServerObject
{
 Person GetPersonInfo(string name,string sex,int age);
}

public interface IServerObjFactory
{
 IServerObject CreateInstance();  
}

我們用WellKnown激活模式注冊遠程對象,在服務器端:

//傳遞對象;
RemotingConfiguration.RegisterWellKnownServiceType(
 typeof(ServerRemoteObject.ServerObjFactory),
 "ServiceMessage",WellKnownObjectMode.SingleCall);

注意這里注冊的不是ServerObject類對象,而是ServerObjFactory類對象。

客戶端:

ServerRemoteObject.IServerObjFactory serverFactory =    
 (ServerRemoteObject.IServerObjFactory) Activator.GetObject(
 typeof(ServerRemoteObject.IServerObjFactory),
 "tcp://localhost:8080/ServiceMessage");

ServerRemoteObject.IServerObject serverObj = serverFactory.CreateInstance();

  為什么說這是一種客戶端激活模式的模擬呢?從激活的方法來看,我們是使用了SingleCall模式來激活對象,但此時激活的并非我們要傳遞的遠程對象,而是工廠對象。如果客戶端要創(chuàng)建遠程對象,還應該通過工廠對象的CreateInstance()方法來獲得。而這個方法正是在客戶端調(diào)用的。因此它的實現(xiàn)方式就等同于客戶端激活模式。

b、利用替代類來取代遠程對象的元數(shù)據(jù)

  實際上,我們可以用一個trick,來欺騙Remoting。這里所說的替代類就是這個trick了。既然是提供服務,Remoting傳遞的遠程對象其實現(xiàn)的細節(jié)當然是放在服務器端。而要在客戶端放對象的副本,不過是因為客戶端必須調(diào)用構(gòu)造函數(shù),而采取的無奈之舉。既然具體的實現(xiàn)是在服務器端,又為了能在客戶端實例化,那么在客戶端就實現(xiàn)這些好了。至于實現(xiàn)的細節(jié),就不用管了。

  如果遠程對象有方法,服務器端則提供方法實現(xiàn),而客戶端就提供這個方法就OK了,至于里面的實現(xiàn),你可以是拋出一個異常,或者return 一個null值;如果方法返回void,那么里面可以是空。關(guān)鍵是這個客戶端類對象要有這個方法。這個方法的實現(xiàn),其實和方法的聲明差不多,所以我說是一個trick。方法如是,構(gòu)造函數(shù)也如此。

還是用代碼來說明這種“陰謀”,更直觀:

服務器端:

public class ServerObject:MarshalByRefObject
{
 public ServerObject()
 {
  
 }

 public Person GetPersonInfo(string name,string sex,int age)
 {
  Person person = new Person();
  person.Name = name;
  person.Sex = sex;
  person.Age = age;
  return person;
 }  
}

客戶端:

public class ServerObject:MarshalByRefObject
{
 public ServerObj()
 {
  throw new System.NotImplementedException();
 }

 public Person GetPersonInfo(string name,string sex,int age)
 {
  throw new System.NotImplementedException();
 }  
}

  比較客戶端和服務器端,客戶端的方法GetPersonInfo(),沒有具體的實現(xiàn)細節(jié),只是拋出了一個異常?;蛘咧苯訉懮险Z句return null,照樣OK。我們稱客戶端的這個類為遠程對象的替代類。

3、利用配置文件實現(xiàn)

  前面所述的方法,于服務器uri、端口、以及激活模式的設(shè)置是用代碼來完成的。其實我們也可以用配置文件來設(shè)置。這樣做有個好處,因為這個配置文件是Xml文檔。如果需要改變端口或其他,我們就不需要修改程序,并重新編譯,而是只需要改變這個配置文件即可。

(1) 服務器端的配置文件:

configuration>
 system.runtime.remoting>
 application name="ServerRemoting">
  service>
  wellknown mode="Singleton" type="ServerRemoteObject.ServerObject" objectUri="ServiceMessage"/>
  /service>
  channels>
   channel ref="tcp" port="8080"/>
  /channels>
 /application>
 /system.runtime.remoting>
/configuration>

如果是客戶端激活模式,則把wellknown改為activated,同時刪除mode屬性。

把該配置文件放到服務器程序的應用程序文件夾中,命名為ServerRemoting.config。那么前面的服務器端程序直接用這條語句即可:

RemotingConfiguration.Configure("ServerRemoting.config");

(2) 客戶端配置文件

如果是客戶端激活模式,修改和上面一樣。調(diào)用也是使用RemotingConfiguration.Configure()方法來調(diào)用存儲在客戶端的配置文件。

配置文件還可以放在machine.config中。如果客戶端程序是web應用程序,則可以放在web.config中。

4、啟動/關(guān)閉指定遠程對象

  Remoting中沒有提供類似UnregisterWellKnownServiceType()的方法,也即是說,一旦通過注冊了遠程對象,如果沒有關(guān)閉通道的話,該對象就一直存在于通道中。只要客戶端激活該對象,就會創(chuàng)建對象實例。如果Remoting傳送的只有一個遠程對象,這不存在問題,關(guān)閉通道就可以了。如果傳送多個遠程對象呢?要關(guān)閉指定的遠程對象應該怎么做?關(guān)閉之后又需要啟動又該如何?

  我們注意到在Remoting中提供了Marshal()和Disconnect()方法,答案就在這里。Marshal()方法是將MarshalByRefObject類對象轉(zhuǎn)化為ObjRef類對象,這個對象是存儲生成代理以與遠程對象通訊所需的所有相關(guān)信息。這樣就可以將該實例序列化以便在應用程序域之間以及通過網(wǎng)絡進行傳輸,客戶端就可以調(diào)用了。而Disconnect()方法則將具體的實例對象從通道中斷開。

方法如下:
首先注冊通道:

TcpChannel channel = new TcpChannel(8080);
ChannelServices.RegisterChannel(channel);

接著啟動服務:
先在服務器端實例化遠程對象。

ServerObject obj = new ServerObject();

然后,注冊該對象。注意這里不用RemotingConfiguration.RegisterWellKnownServiceType(),而是使用RemotingServices.Marshal():

ObjRef objrefWellKnown = RemotingServices.Marshal(obj, "ServiceMessage");

如果要注銷對象,則:

RemotingServices.Disconnect(obj);

  要注意,這里Disconnect的類對象必須是前面實例化的對象。正因為此,我們可以根據(jù)需要創(chuàng)建指定的遠程對象,而關(guān)閉時,則Disconnect之前實例化的對象。

  至于客戶端的調(diào)用,和前面WellKnown模式的方法相同,仍然是通過Activator.GetObject()來獲得。但從實現(xiàn)代碼來看,我們會注意到一個問題,由于服務器端是顯式的實例化了遠程對象,因此不管客戶端有多少,是否相同,它們調(diào)用的都是同一個遠程對象。因此我們將這個方法稱為模擬的SingleTon模式。

客戶端激活模式

  我們也可以通過Marshal()和Disconnect()來模擬客戶端激活模式。首先我們來回顧“遠程對象元數(shù)據(jù)相關(guān)性”一節(jié),在這一節(jié)中,我說到采用設(shè)計模式的“抽象工廠”來創(chuàng)建對象實例,以此用SingleCall模式來模擬客戶端激活模式。在仔細想想前面的模擬的SingleTon模式。是不是答案就將呼之欲出呢?

  在“模擬的SingleTon”模式中,我們是將具體的遠程對象實例進行Marshal,以此讓客戶端獲得該對象的引用信息。那么我們換一種思路,當我們用抽象工廠提供接口,工廠類實現(xiàn)創(chuàng)建遠程對象的方法。然后我們在服務器端創(chuàng)建工廠類實例。再將這個工廠類實例進行Marshal。而客戶端獲取對象時,不是獲取具體的遠程對象,而是獲取具體的工廠類對象。然后再調(diào)用CreateInstance()方法來創(chuàng)建具體的遠程對象實例。此時,對于多個客戶端而言,調(diào)用的是同一個工廠類對象;然而遠程對象是在各個客戶端自己創(chuàng)建的,因此對于遠程對象而言,則是由客戶端激活,創(chuàng)建的是不同對象了。

當我們要啟動/關(guān)閉指定對象時,只需要用Disconnet()方法來注銷工廠類對象就可以了。

六、小結(jié)

  Microsoft.Net Remoting真可以說是博大精深。整個Remoting的內(nèi)容不是我這一篇小文所能盡述的,更不是我這個Remoting的初學者所能掌握的。王國維在《人間詞話》一書中寫到:古今之成大事業(yè)大學問者,必經(jīng)過三種境界。“昨夜西風凋碧樹,獨上高樓,望盡天涯路。”此第一境界也?!耙聨u寬終不悔,為伊消得人憔悴?!贝说诙辰缫病!氨娎飳にО俣?,驀然回首,那人卻在燈火闌珊處。”此第三境界也。如以此來形容我對Remoting的學習,還處于“獨上高樓,望盡天涯路”的時候,真可以說還未曾登堂入室。

  或許需得“衣帶漸寬”,學得Remoting“終不悔”,方才可以“驀然回首”吧。

  以上就是.Net Remoting基礎(chǔ)的全部內(nèi)容,希望能給大家一個參考,也希望大家多多支持腳本之家。

您可能感興趣的文章:
  • Microsoft .Net Remoting系列教程之三:Remoting事件處理全接觸
  • Microsoft .Net Remoting系列教程之二:Marshal、Disconnect與生命周期以及跟蹤服務
  • Flex 錯誤(mx.messaging.messages::RemotingMessage)分析
  • ASP.NET通過Remoting service上傳文件
  • java webservice上傳下載文件代碼分享
  • java通過客戶端訪問服務器webservice的方法
  • ASP.NET使用WebService實現(xiàn)天氣預報功能
  • jQuery調(diào)用Webservice傳遞json數(shù)組的方法
  • C# WebService發(fā)布以及IIS發(fā)布
  • Remoting和Webservice的詳細介紹及區(qū)別

標簽:玉林 佳木斯 喀什 房產(chǎn) 呂梁 德宏 泰安 巨人網(wǎng)絡通訊聲明:本文標題《Microsoft .Net Remoting系列教程之一:.Net Remoting基礎(chǔ)篇》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡,涉及言論、版權(quán)與本站無關(guān)。

  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    仙桃市| 旬阳县| 竹溪县| 斗六市| 永宁县| 甘肃省| 襄樊市| 垣曲县| 遂宁市| 五莲县| 铁力市| 会理县| 凤城市| 深圳市| 吉木乃县| 桐乡市| 涟源市| 保定市| 苏尼特右旗| 永城市| 青铜峡市| 阿拉善右旗| 绥棱县| 肇东市| 平顺县| 喀喇| 旬邑县| 崇信县| 榕江县| 邳州市| 吉水县| 达拉特旗| 双江| 沛县| 九江市| 桐梓县| 南投县| 漳平市| 蒙山县| 济源市| 伊川县|