

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、<p> Microsoft .Net Remoting系列專題之一</p><p> 一、Remoting基礎</p><p> 什么是Remoting,簡而言之,我們可以將其看作是一種分布式處理方式。從微軟的產品角度來看,可以說Remoting就是DCOM的一種升級,它改善了很多功能,并極好的融合到.Net平臺下。Microsoft® .NET Remotin
2、g 提供了一種允許對象通過應用程序域與另一對象進行交互的框架。這也正是我們使用Remoting的原因。為什么呢?在Windows操作系統(tǒng)中,是將應用程序分離為單獨的進程。這個進程形成了應用程序代碼和數據周圍的一道邊界。如果不采用進程間通信(RPC)機制,則在一個進程中執(zhí)行的代碼就不能訪問另一進程。這是一種操作系統(tǒng)對應用程序的保護機制。然而在某些情況下,我們需要跨過應用程序域,與另外的應用程序域進行通信,即穿越邊界。</p>
3、<p> 在Remoting中是通過通道(channel)來實現兩個應用程序域之間對象的通信的。如圖所示:</p><p> 首先,客戶端通過Remoting,訪問通道以獲得服務端對象,再通過代理解析為客戶端對象。這就提供一種可能性,即以服務的方式來發(fā)布服務器對象。遠程對象代碼可以運行在服務器上(如服務器激活的對象和客戶端激活的對象),然后客戶端再通過Remoting連接服務器,獲得該服務對象并通
4、過序列化在客戶端運行。</p><p> 在Remoting中,對于要傳遞的對象,設計者除了需要了解通道的類型和端口號之外,無需再了解數據包的格式。但必須注意的是,客戶端在獲取服務器端對象時,并不是獲得實際的服務端對象,而是獲得它的引用。這既保證了客戶端和服務器端有關對象的松散耦合,同時也優(yōu)化了通信的性能。</p><p> 1、Remoting的兩種通道</p><
5、;p> Remoting的通道主要有兩種:Tcp和Http。在.Net中,System.Runtime.Remoting.Channel中定義了IChannel接口。IChannel接口包括了TcpChannel通道類型和Http通道類型。它們分別對應Remoting通道的這兩種類型。</p><p> TcpChannel類型放在名字空間System.Runtime.Remoting.Channel.
6、Tcp中。Tcp通道提供了基于Socket的傳輸工具,使用Tcp協(xié)議來跨越Remoting邊界傳輸序列化的消息流。TcpChannel類型默認使用二進制格式序列化消息對象,因此它具有更高的傳輸性能。HttpChannel類型放在名字空間System.Runtime.Remoting.Channel.Http中。它提供了一種使用Http協(xié)議,使其能在Internet上穿越防火墻傳輸序列化消息流。默認情況下,HttpChannel類型使用S
7、oap格式序列化消息對象,因此它具有更好的互操作性。通常在局域網內,我們更多地使用TcpChannel;如果要穿越防火墻,則使用HttpChannel。</p><p> 2、遠程對象的激活方式</p><p> 在訪問遠程類型的一個對象實例之前,必須通過一個名為Activation的進程創(chuàng)建它并進行初始化。這種客戶端通過通道來創(chuàng)建遠程對象,稱為對象的激活。在Remoting中,遠程對
8、象的激活分為兩大類:服務器端激活和客戶端激活。</p><p> (1) 服務器端激活,又叫做WellKnow方式,很多又翻譯為知名對象。為什么稱為知名對象激活模式呢?是因為服務器應用程序在激活對象實例之前會在一個眾所周知的統(tǒng)一資源標識符(URI)上來發(fā)布這個類型。然后該服務器進程會為此類型配置一個WellKnown對象,并根據指定的端口或地址來發(fā)布對象。.Net Remoting把服務器端激活又分為Singl
9、eTon模式和SingleCall模式兩種。</p><p> SingleTon模式:此為有狀態(tài)模式。如果設置為SingleTon激活方式,則Remoting將為所有客戶端建立同一個對象實例。當對象處于活動狀態(tài)時,SingleTon實例會處理所有后來的客戶端訪問請求,而不管它們是同一個客戶端,還是其他客戶端。SingleTon實例將在方法調用中一直維持其狀態(tài)。舉例來說,如果一個遠程對象有一個累加方法(i=0;
10、++i),被多個客戶端(例如兩個)調用。如果設置為SingleTon方式,則第一個客戶獲得值為1,第二個客戶獲得值為2,因為他們獲得的對象實例是相同的。如果熟悉Asp.Net的狀態(tài)管理,我們可以認為它是一種Application狀態(tài)。</p><p> SingleCall模式:SingleCall是一種無狀態(tài)模式。一旦設置為SingleCall模式,則當客戶端調用遠程對象的方法時,Remoting會為每一個客
11、戶端建立一個遠程對象實例,至于對象實例的銷毀則是由GC自動管理的。同上一個例子而言,則訪問遠程對象的兩個客戶獲得的都是1。我們仍然可以借鑒Asp.Net的狀態(tài)管理,認為它是一種Session狀態(tài)。</p><p> (2) 客戶端激活。與WellKnown模式不同,Remoting在激活每個對象實例的時候,會給每個客戶端激活的類型指派一個URI??蛻舳思せ钅J揭坏┇@得客戶端的請求,將為每一個客戶端都建立一個實例
12、引用。SingleCall模式和客戶端激活模式是有區(qū)別的:首先,對象實例創(chuàng)建的時間不一樣??蛻舳思せ罘绞绞强蛻粢坏┌l(fā)出調用的請求,就實例化;而SingleCall則是要等到調用對象方法時再創(chuàng)建。其次,SingleCall模式激活的對象是無狀態(tài)的,對象生命期的管理是由GC管理的,而客戶端激活的對象則有狀態(tài),其生命周期可自定義。其三,兩種激活模式在服務器端和客戶端實現的方法不一樣。尤其是在客戶端,SingleCall模式是由GetObjec
13、t()來激活,它調用對象默認的構造函數。而客戶端激活模式,則通過CreateInstance()來激活,它可以傳遞參數,所以可以調用自定義的構造函數來創(chuàng)建實例。</p><p><b> 二、遠程對象的定義</b></p><p> 前面講到,客戶端在獲取服務器端對象時,并不是獲得實際的服務端對象,而是獲得它的引用。因此在Remoting中,對于遠程對象有一些必須
14、的定義規(guī)范要遵循。</p><p> 由于Remoting傳遞的對象是以引用的方式,因此所傳遞的遠程對象類必須繼承MarshalByRefObject。MSDN對MarshalByRefObject的說明是:MarshalByRefObject 是那些通過使用代理交換消息來跨越應用程序域邊界進行通信的對象的基類。不是從 MarshalByRefObject 繼承的對象會以隱式方式按值封送。當遠程應用程序引用一個
15、按值封送的對象時,將跨越遠程處理邊界傳遞該對象的副本。因為您希望使用代理方法而不是副本方法進行通信,因此需要繼承MarshallByRefObject。</p><p> 以下是一個遠程對象類的定義:public class ServerObject:MarshalByRefObject{ public Person Ge
16、tPersonInfo(string name,string sex,int age) { Person person = new Person();
17、0; person.Name = name; person.Sex = sex; person.Age = ag
18、e; return person; }}</p><p> 這個類只實現了最簡單的方法,就是設置一個人的基本信息,并返回一個Person類對象。注意這里返回的Person類。由于這里所傳遞的P
19、erson則是以傳值的方式來完成的,而Remoting要求必須是引用的對象,所以必須將Person類序列化。</p><p> 因此,在Remoting中的遠程對象中,如果還要調用或傳遞某個對象,例如類,或者結構,則該類或結構則必須實現串行化Attribute[SerializableAttribute]:[Serializable] public class Person {
20、60; public Person() { }</p&g
21、t;<p> private string name; private string sex; private int age;</p><p> public string Name
22、160; { get {return name;} set {n
23、ame = value;} }</p><p> public string Sex { get {retu
24、rn sex;} set {sex = value;} }</p><p> public int Age
25、{ get {return age;} set {age = value;} }
26、160; }將該遠程對象以類庫的方式編譯成Dll。這個Dll將分別放在服務器端和客戶端,以添加引用。</p><p> 在Remoting中能夠傳遞的遠程對象可以是各種類型,包括復雜的DataSet對象,只要它能夠被序列化。遠程對象也可以包含事件,但服務器端對于事件的處理比較特殊,我將在本系列之三中介紹。</p><p><b> 三、服務器端</b>
27、</p><p> 根據第一部分所述,根據激活模式的不同,通道類型的不同服務器端的實現方式也有所不同。大體上說,服務器端應分為三步:</p><p><b> 1、注冊通道</b></p><p> 要跨越應用程序域進行通信,必須實現通道。如前所述,Remoting提供了IChannel接口,分別包含TcpChannel和HttpChan
28、nel兩種類型的通道。這兩種類型除了性能和序列化數據的格式不同外,實現的方式完全一致,因此下面我們就以TcpChannel為例。</p><p> 注冊TcpChannel,首先要在項目中添加引用“System.Runtime.Remoting”,然后using名字空間:System.Runtime.Remoting.Channel.Tcp。代碼如下:
29、; TcpChannel channel = new TcpChannel(8080); ChannelServices.RegisterChannel(channel);</p><p> 在實例化通道對象
30、時,將端口號作為參數傳遞。然后再調用靜態(tài)方法RegisterChannel()來注冊該通道對象即可。</p><p><b> 2、注冊遠程對象</b></p><p> 注冊了通道后,要能激活遠程對象,必須在通道中注冊該對象。根據激活模式的不同,注冊對象的方法也不同。</p><p> (1) SingleTon模式</p>
31、<p> 對于WellKnown對象,可以通過靜態(tài)方法RemotingConfiguration.RegisterWellKnownServiceType()來實現:RemotingConfiguration.RegisterWellKnownServiceType(
32、; typeof(ServerRemoteObject.ServerObject), "ServiceMessage",WellKnownObjectMode.SingleTon);</p><
33、p> (2)SingleCall模式</p><p> 注冊對象的方法基本上和SingleTon模式相同,只需要將枚舉參數WellKnownObjectMode改為SingleCall就可以了。RemotingConfiguration.RegisterWellKnownServiceType(
34、 typeof(ServerRemoteObject.ServerObject), "ServiceMessage",WellKnownObjectMode.Sing
35、leCall);</p><p> (3)客戶端激活模式</p><p> 對于客戶端激活模式,使用的方法又有不同,但區(qū)別不大,看了代碼就一目了然。RemotingConfiguration.ApplicationName = "ServiceMessage";RemotingConfiguration.RegisterActivatedServiceType(
36、 typeof(ServerRemoteObject.ServerObject));</p><p> 為什么要在注冊對象方法前設置ApplicationName屬性呢?其實這個屬性就是該對象的URI。對于WellKnown模式,U
37、RI是放在RegisterWellKnownServiceType()方法的參數中,當然也可以拿出來專門對ApplicationName屬性賦值。而RegisterActivatedServiceType()方法的重載中,沒有ApplicationName的參數,所以必須分開。</p><p><b> 3、注銷通道</b></p><p> 如果要關閉Remot
38、ing的服務,則需要注銷通道,也可以關閉對通道的監(jiān)聽。在Remoting中當我們注冊通道的時候,就自動開啟了通道的監(jiān)聽。而如果關閉了對通道的監(jiān)聽,則該通道就無法接受客戶端的請求,但通道仍然存在,如果你想再一次注冊該通道,會拋出異常。</p><p> //獲得當前已注冊的通道;
39、IChannel[] channels = ChannelServices.RegisteredChannels;</p><p> //關閉指定名為MyTcp的通道; foreach (IChannel eachChannel in channels)
40、 { if (eachChannel.ChannelName == "MyTcp")
41、0; { TcpChannel tcpChannel = (TcpChanne
42、l)eachChannel;</p><p> //關閉監(jiān)聽; tcpChannel.StopListening(null);</p><p> //注銷通道
43、; ChannelServices.UnregisterChannel(tcpChannel);
44、160; } }代碼中,RegisterdChannel屬性獲得的是當前已注冊的通道。在Remoting中,是允許同時注冊多個通道的,這一點會在后面說明。</p><p><b> 四、客戶端</b&g
45、t;</p><p> 客戶端主要做兩件事,一是注冊通道。這一點從圖一就可以看出,Remoting中服務器端和客戶端都必須通過通道來傳遞消息,以獲得遠程對象。第二步則是獲得該遠程對象。</p><p> 1、注冊通道:TcpChannel channel = new TcpChannel();ChannelServices.RegisterChannel(channel);<
46、/p><p> 注意在客戶端實例化通道時,是調用的默認構造函數,即沒有傳遞端口號。事實上,這個端口號是缺一不可的,只不過它的指定被放在后面作為了Uri的一部分。2、獲得遠程對象。</p><p> 與服務器端相同,不同的激活模式決定了客戶端的實現方式也將不同。不過這個區(qū)別僅僅是WellKnown激活模式和客戶端激活模式之間的區(qū)別,而對于SingleTon和SingleCall模式,客戶
47、端的實現完全相同。</p><p> (1) WellKnown激活模式</p><p> 要獲得服務器端的知名遠程對象,可通過Activator進程的GetObject()方法來獲得:ServerRemoteObject.ServerObject serverObj = (ServerRemoteObject.ServerObject)Activator.GetObject(
48、160; typeof(ServerRemoteObject.ServerObject), "tcp://localhost:8080/ServiceMessage");</p><p> 首先以WellKnown模式激活,客戶端獲得對象的方法是使用
49、GetObject()。其中參數第一個是遠程對象的類型。第二個參數就是服務器端的uri。如果是http通道,自然是用http://localhost:8080/ServiceMessage了。因為我是用本地機,所以這里是localhost,你可以用具體的服務器IP地址來代替它。端口必須和服務器端的端口一致。后面則是服務器定義的遠程對象服務名,即ApplicationName屬性的內容。(2) 客戶端激活模式</p>&
50、lt;p> 如前所述,WellKnown模式在客戶端創(chuàng)建對象時,只能調用默認的構造函數,上面的代碼就說明了這一點,因為GetObject()方法不能傳遞構造函數的參數。而客戶端激活模式則可以通過自定義的構造函數來創(chuàng)建遠程對象。</p><p> 客戶端激活模式有兩種方法:1) 調用RemotingConfiguration的靜態(tài)方法RegisterActivatedClientType()。這個方法返
51、回值為Void,它只是將遠程對象注冊在客戶端而已。具體的實例化還需要調用對象類的構造函數。 RemotingConfiguration.RegisterActivatedClientType(
52、60; typeof(ServerRemoteObject.ServerObject), "tcp://localhost:80
53、80/ServiceMessage"); ServerRemoteObject.ServerObject serverObj = new ServerRemoteObject.ServerObject();</p><p> 2) 調用進程Activator的CreateInstance()方法。這個方法將創(chuàng)建方法參數指定類型的類對象。它與前面的GetObject()不同的是,它要在客戶端
54、調用構造函數,而GetObject()只是獲得對象,而創(chuàng)建實例是在服務器端完成的。CreateInstance()方法有很多個重載,我著重說一下其中常用的兩個。a、 public static object CreateInstance(Type type, object[] args, object[] activationAttributes);</p><p> 參數說明:type:要創(chuàng)建的
55、對象的類型。args :與要調用構造函數的參數數量、順序和類型匹配的參數數組。如果 args 為空數組或空引用(Visual Basic 中為 Nothing),則調用不帶任何參數的構造函數(默認構造函數)。activationAttributes :包含一個或多個可以參與激活的屬性的數組。</p><p> 這里的參數args是一個object[]數組類型。它可以傳遞要創(chuàng)建對象的構造函數中的參數。從這里其
56、實可以得到一個結論:WellKnown激活模式所傳遞的遠程對象類,只能使用默認的構造函數;而Activated模式則可以用戶自定義構造函數。activationAttributes參數在這個方法中通常用來傳遞服務器的url。假設我們的遠程對象類ServerObject有個構造函數: ServerObje
57、ct(string pName,string pSex,int pAge) { name = pName;
58、160; sex = pSex; age = pAge;
59、0; }</p><p> 那么實現的代碼是: object[] attrs = {new UrlAttribute("tcp://localhost:8080/ServiceMessa
60、ge")}; object[] objs = new object[3]; objs[0] = "wayfarer";
61、; objs[1] = "male"; objs[2] = 28; &
62、#160; ServerRemoteObject.ServerObject = Activator.CreateInstance( typeof(ServerRemoteObject.ServerObject),objs,attrs);可以看到
63、,objs[]數組傳遞的就是構造函數的參數。</p><p> b、public static ObjectHandle CreateInstance(string assemblyName, string typeName, object[] activationAttribute);</p><p> 參數說明:assemblyName :將在其中查找名為 typeName 的類
64、型的程序集的名稱。如果 assemblyName 為空引用(Visual Basic 中為 Nothing),則搜索正在執(zhí)行的程序集。typeName:首選類型的名稱。activationAttributes :包含一個或多個可以參與激活的屬性的數組。</p><p> 參數說明一目了然。注意這個方法返回值為ObjectHandle類型,因此代碼與前不同: &
65、#160; object[] attrs = {new UrlAttribute("tcp://localhost:8080/EchoMessage")};
66、; ObjectHandle handle = Activator.CreateInstance("ServerRemoteObject", &
67、#160; "ServerRemoteObject.ServerObject",attrs);
68、 ServerRemoteObject.ServerObject obj = (ServerRemoteObject.ServerObject)handle.Unwrap();</p><p> 這個方法實際上是調用的默認構造函數。ObjectHandle.Unwrap()方法是返回被包裝的對象。</p><p> 說明:要使用UrlAttribute,還需要在命名空間中添加:usin
69、g System.Runtime.Remoting.Activation;</p><p> 五、Remoting基礎的補充</p><p> 通過上面的描述,基本上已經完成了一個最簡單的Remoting程序。這是一個標準的創(chuàng)建Remoting程序的方法,但在實際開發(fā)過程中,我們遇到的情況也許千奇百怪,如果只掌握一種所謂的“標準”,就妄想可以“一招鮮、吃遍天”,是不可能的。</p
70、><p><b> 1、注冊多個通道</b></p><p> 在Remoting中,允許同時創(chuàng)建多個通道,即根據不同的端口創(chuàng)建不同的通道。但是,Remoting要求通道的名字必須不同,因為它要用來作為通道的唯一標識符。雖然IChannel有ChannelName屬性,但這個屬性是只讀的。因此前面所述的創(chuàng)建通道的方法無法實現同時注冊多個通道的要求。</p>
71、<p> 這個時候,我們必須用到System.Collection中的IDictionary接口:</p><p> 注冊Tcp通道:IDictionary tcpProp = new Hashtable();tcpProp["name"] = "tcp9090";tcpProp["port"] = 9090;IChannel
72、channel = new TcpChannel(tcpProp, new BinaryClientFormatterSinkProvider(), new BinaryServerFormatterSinkProvider());ChannelServices.RegisterChannel(channel);</p><p> 注冊Http通道:IDictionary httpP
73、rop = new Hashtable();httpProp["name"] = "http8080";httpProp["port"] = 8080;IChannel channel = new HttpChannel(httpProp, new SoapClientFormatterSinkProvider(), new SoapServerF
74、ormatterSinkProvider());ChannelServices.RegisterChannel(channel);</p><p> 在name屬性中,定義不同的通道名稱就可以了。</p><p> 2、遠程對象元數據相關性</p><p> 由于服務器端和客戶端都要用到遠程對象,通常的方式是生成兩份完全相同的對象Dll,分別添加引用。不過為
75、了代碼的安全性,且降低客戶端對遠程對象元數據的相關性,我們有必要對這種方式進行改動。即在服務器端實現遠程對象,而在客戶端則刪除這些實現的元數據。</p><p> 由于激活模式的不同,在客戶端創(chuàng)建對象的方法也不同,所以要分離元數據的相關性,也應分為兩種情況。</p><p> (1) WellKnown激活模式:</p><p> 通過接口來實現。在服務器端,
76、提供接口和具體類的實現,而在客戶端僅提供接口: public interface IServerObject { Person GetPersonInfo(string name,string sex,int age); }</p&g
77、t;<p> public class ServerObject:MarshalByRefObject,IServerObject{ ......}注意:兩邊生成該對象程序集的名字必須相同,嚴格地說,是命名空間的名字必須相同。 (2) 客戶端激活模式:</p><p
78、> 如前所述,對于客戶端激活模式,不管是使用靜態(tài)方法,還是使用CreateInstance()方法,都必須在客戶端調用構造函數實例化對象。所以,在客戶端我們提供的遠程對象,就不能只提供接口,而沒有類的實現。實際上,要做到與遠程對象元數據的分離,可以由兩種方法供選擇:</p><p> a、利用WellKnown激活模式模擬客戶端激活模式:</p><p> 方法是利用設計模式中
79、的“抽象工廠”,下面的類圖表描述了總體解決方案:</p><p> 我們在服務器端的遠程對象中加上抽象工廠的接口和實現類: public interface IServerObject { Person GetPersonInfo(string nam
80、e,string sex,int age); }</p><p> public interface IServerObjFactory { IServerObject CreateInstance();
81、 }</p><p> public class ServerObject:MarshalByRefObject,IServerObject { public Person GetPersonInfo(string
82、name,string sex,int age) { Person person = new Person();
83、; person.Name = name; person.Sex = sex; person.Age = age;
84、0; return person; } }</p><p> public class ServerObjFa
85、ctory:MarshalByRefObject,IServerObjFactory { public IServerObject CreateInstance() {
86、 return new ServerObject(); } }</p><p> 然后再客戶端的遠程對象中只提供工廠接口和原來的對象接口: public interface ISe
87、rverObject { Person GetPersonInfo(string name,string sex,int age); }</p><p> public interface IServerObjFactory &
88、#160; { IServerObject CreateInstance(); }我們用WellKnown激活模式注冊遠程對象,在服務器端:
89、160; //傳遞對象; RemotingConfiguration.RegisterWellKnownServiceType(
90、0; typeof(ServerRemoteObject.ServerObjFactory), "ServiceMessage",WellKnownObjectMode.SingleCall);</p>
91、<p> 注意這里注冊的不是ServerObject類對象,而是ServerObjFactory類對象。</p><p> 客戶端:ServerRemoteObject.IServerObjFactory serverFactory =
92、160; (ServerRemoteObject.IServerObjFactory) Activator.GetObject(
93、; typeof(ServerRemoteObject.IServerObjFactory), "tcp://localhost:8080/ServiceMessage&quo
94、t;);</p><p> ServerRemoteObject.IServerObject serverObj = serverFactory.CreateInstance();</p><p> 為什么說這是一種客戶端激活模式的模擬呢?從激活的方法來看,我們是使用了SingleCall模式來激活對象,但此時激活的并非我們要傳遞的遠程對象,而是工廠對象。如果客戶端要創(chuàng)建遠程對象,還應
95、該通過工廠對象的CreateInstance()方法來獲得。而這個方法正是在客戶端調用的。因此它的實現方式就等同于客戶端激活模式。</p><p> b、利用替代類來取代遠程對象的元數據</p><p> 實際上,我們可以用一個trick,來欺騙Remoting。這里所說的替代類就是這個trick了。既然是提供服務,Remoting傳遞的遠程對象其實現的細節(jié)當然是放在服務器端。而要在客
96、戶端放對象的副本,不過是因為客戶端必須調用構造函數,而采取的無奈之舉。既然具體的實現是在服務器端,又為了能在客戶端實例化,那么在客戶端就實現這些好了。至于實現的細節(jié),就不用管了。</p><p> 如果遠程對象有方法,服務器端則提供方法實現,而客戶端就提供這個方法就OK了,至于里面的實現,你可以是拋出一個異常,或者return 一個null值;如果方法返回void,那么里面可以是空。關鍵是這個客戶端類對象要有這
97、個方法。這個方法的實現,其實和方法的聲明差不多,所以我說是一個trick。方法如是,構造函數也如此。</p><p> 還是用代碼來說明這種“陰謀”,更直觀:</p><p> 服務器端: public class ServerObject:MarshalByRefObject {
98、160; public ServerObject() { }</p>&
99、lt;p> public Person GetPersonInfo(string name,string sex,int age) { Person person = new Person();
100、160; person.Name = name; person.Sex = sex;
101、160; person.Age = age; return person; } }&l
102、t;/p><p> 客戶端: public class ServerObject:MarshalByRefObject { public ServerObj() {
103、160; throw new System.NotImplementedException(); }</p><p> public Person GetPersonInfo(string name,stri
104、ng sex,int age) { throw new System.NotImplementedException(); }
105、0; }</p><p> 比較客戶端和服務器端,客戶端的方法GetPersonInfo(),沒有具體的實現細節(jié),只是拋出了一個異常?;蛘咧苯訉懮险Z句return null,照樣OK。我們稱客戶端的這個類為遠程對象的替代類。</p><p> 3、利用配置文件實現</p&
106、gt;<p> 前面所述的方法,于服務器uri、端口、以及激活模式的設置是用代碼來完成的。其實我們也可以用配置文件來設置。這樣做有個好處,因為這個配置文件是Xml文檔。如果需要改變端口或其他,我們就不需要修改程序,并重新編譯,而是只需要改變這個配置文件即可。</p><p> (1) 服務器端的配置文件:<configuration> <
107、system.runtime.remoting> <application name="ServerRemoting"> <service> <wel
108、lknown mode="Singleton" type="ServerRemoteObject.ServerObject" objectUri="ServiceMessage"/> </service> &
109、;lt;channels> <channel ref="tcp" port="8080"/> </channels> <
110、;/application> </system.runtime.remoting></configuration></p><p> 如果是客戶端激活模式,則把wellknown改為activated,同時刪除mode屬性。</p><p> 把該配置文件放到服務器程序的應用程序文件夾中,命名為Ser
111、verRemoting.config。那么前面的服務器端程序直接用這條語句即可:RemotingConfiguration.Configure("ServerRemoting.config");</p><p> (2) 客戶端配置文件</p><p> 如果是客戶端激活模式,修改和上面一樣。調用也是使用RemotingConfiguration.Configur
112、e()方法來調用存儲在客戶端的配置文件。</p><p> 配置文件還可以放在machine.config中。如果客戶端程序是web應用程序,則可以放在web.config中。</p><p> 4、啟動/關閉指定遠程對象</p><p> Remoting中沒有提供類似UnregisterWellKnownServiceType()的方法,也即是說,一旦通過
113、注冊了遠程對象,如果沒有關閉通道的話,該對象就一直存在于通道中。只要客戶端激活該對象,就會創(chuàng)建對象實例。如果Remoting傳送的只有一個遠程對象,這不存在問題,關閉通道就可以了。如果傳送多個遠程對象呢?要關閉指定的遠程對象應該怎么做?關閉之后又需要啟動又該如何?</p><p> 我們注意到在Remoting中提供了Marshal()和Disconnect()方法,答案就在這里。Marshal()方法是將Ma
114、rshalByRefObject類對象轉化為ObjRef類對象,這個對象是存儲生成代理以與遠程對象通訊所需的所有相關信息。這樣就可以將該實例序列化以便在應用程序域之間以及通過網絡進行傳輸,客戶端就可以調用了。而Disconnect()方法則將具體的實例對象從通道中斷開。</p><p> 方法如下:首先注冊通道:TcpChannel channel = new TcpChannel(8080);Chann
115、elServices.RegisterChannel(channel);</p><p> 接著啟動服務:先在服務器端實例化遠程對象。ServerObject obj = new ServerObject();</p><p> 然后,注冊該對象。注意這里不用RemotingConfiguration.RegisterWellKnownServiceType(),而是使用Remot
116、ingServices.Marshal():</p><p> ObjRef objrefWellKnown = RemotingServices.Marshal(obj, "ServiceMessage");</p><p> 如果要注銷對象,則:RemotingServices.Disconnect(obj);</p><p> 要注
117、意,這里Disconnect的類對象必須是前面實例化的對象。正因為此,我們可以根據需要創(chuàng)建指定的遠程對象,而關閉時,則Disconnect之前實例化的對象。</p><p> 至于客戶端的調用,和前面WellKnown模式的方法相同,仍然是通過Activator.GetObject()來獲得。但從實現代碼來看,我們會注意到一個問題,由于服務器端是顯式的實例化了遠程對象,因此不管客戶端有多少,是否相同,它們調用的
118、都是同一個遠程對象。因此我們將這個方法稱為模擬的SingleTon模式。</p><p><b> 客戶端激活模式</b></p><p> 我們也可以通過Marshal()和Disconnect()來模擬客戶端激活模式。首先我們來回顧“遠程對象元數據相關性”一節(jié),在這一節(jié)中,我說到采用設計模式的“抽象工廠”來創(chuàng)建對象實例,以此用SingleCall模式來模擬客戶
119、端激活模式。在仔細想想前面的模擬的SingleTon模式。是不是答案就將呼之欲出呢?</p><p> 在“模擬的SingleTon”模式中,我們是將具體的遠程對象實例進行Marshal,以此讓客戶端獲得該對象的引用信息。那么我們換一種思路,當我們用抽象工廠提供接口,工廠類實現創(chuàng)建遠程對象的方法。然后我們在服務器端創(chuàng)建工廠類實例。再將這個工廠類實例進行Marshal。而客戶端獲取對象時,不是獲取具體的遠程對象,
120、而是獲取具體的工廠類對象。然后再調用CreateInstance()方法來創(chuàng)建具體的遠程對象實例。此時,對于多個客戶端而言,調用的是同一個工廠類對象;然而遠程對象是在各個客戶端自己創(chuàng)建的,因此對于遠程對象而言,則是由客戶端激活,創(chuàng)建的是不同對象了。</p><p> 當我們要啟動/關閉指定對象時,只需要用Disconnet()方法來注銷工廠類對象就可以了。</p><p><b&g
121、t; 六、小結</b></p><p> Microsoft.Net Remoting真可以說是博大精深。整個Remoting的內容不是我這一篇小文所能盡述的,更不是我這個Remoting的初學者所能掌握的。王國維在《人間詞話》一書中寫到:古今之成大事業(yè)大學問者,必經過三種境界?!白蛞刮黠L凋碧樹,獨上高樓,望盡天涯路。”此第一境界也?!耙聨u寬終不悔,為伊消得人憔悴?!贝说诙辰缫病!氨娎飳にО?/p>
122、度,驀然回首,那人卻在燈火闌珊處?!贝说谌辰缫?。如以此來形容我對Remoting的學習,還處于“獨上高樓,望盡天涯路”的時候,真可以說還未曾登堂入室。</p><p> 或許需得“衣帶漸寬”,學得Remoting“終不悔”,方才可以“驀然回首”吧。</p><p> posted on 2004-07-30 20:44 張逸 閱讀(69253) 評論(152) 編輯 收藏
123、 網摘 所屬分類: .NET Remoting</p><p> Microsoft .Net Remoting系列專題之二:Marshal、Disconnect與生命周期以及跟蹤服務 </p><p> 我寫的.Net Remoting系列專題:</p><p> Microsoft .Net Remoting系列專題之一:.Net Remoting基礎篇&
124、lt;/p><p> Microsoft .Net Remoting系列專題之三:Remoting事件處理全接觸</p><p> Microsoft .Net Remoting系列專題之二 </p><p><b> 一、遠程對象的激活</b></p><p> 在Remoting中有三種激活方式,一般的
125、實現是通過RemotingServices類的靜態(tài)方法來完成。工作過程事實上是將該遠程對象注冊到通道中。由于Remoting沒有提供與之對應的Unregister方法來注銷遠程對象,所以如果需要注冊/注銷指定對象,微軟推薦使用Marshal(一般譯為編組)和Disconnect配對使用。在《Net Remoting基礎篇》中我已經談到:Marshal()方法是將MarshalByRefObject類對象轉化為ObjRef類對象,這個對象
126、是存儲生成代理以與遠程對象通訊所需的所有相關信息。這樣就可以將該實例序列化以便在應用程序域之間以及通過網絡進行傳輸,客戶端就可以調用了。而Disconnect()方法則將具體的實例對象從通道中斷開。</p><p> 根據上述說明,Marshal()方法對遠程對象以引用方式進行編組(Marshal-by-Reference,MBR),并將對象的代理信息放到通道中。客戶端可以通過Activator.GetObje
127、ct()來獲取。如果用戶要注銷該對象,則通過調用Disconnect()方法。那么這種方式對于編組的遠程對象是否存在生命周期的管理呢?這就是本文所要描述的問題。</p><p><b> 二、生命周期</b></p><p> 在CLR中,框架提供了GC(垃圾回收器)來管理內存中對象的生命周期。同樣的,.Net Remoting使用了一種分布式垃圾回收,基于租用的
128、形式來管理遠程對象的生命周期。</p><p> 早期的DCOM對于對象生命周期的管理是通過ping和引用計數來確定對象何時應當作為垃圾回收。然而ping引起的網絡流量對分布式應用程序的性能是一種痛苦的負擔,它大大地影響了分布式處理的整體性能。.Net Remoting在每個應用程序域中都引入一個租用管理器,為每個服務器端的SingleTon,或每個客戶端激活的遠程對象保存著對租用對象的引用。(說明:對于服務器
129、端激活的SingleCall方式,由于它是無狀態(tài)的,對于每個激活的遠程對象,都由CLR的GC來自動回收,因此對于SingleCall模式激活的遠程對象,不存在生命周期的管理。)</p><p><b> 1、租用</b></p><p> 租用是個封裝了TimeSpan值的對象,用以管理遠程對象的生存期。在.Net Remoting中提供了定義租用功能的ILeas
130、e接口。當Remoting通過SingleTon模式或客戶端激活模式來激活遠程對象時,租用對象調用從System.MarshalByRefObject繼承的InitializeLifetimeService方法,向對象請求租用。</p><p> ILease接口定義了有關生命周期的屬性,均為TimeSpan值。如下:InitialLeaseTime:初始化有效時間,默認值為300秒,如果為0,表示永不過期;
131、RenewOnCallTime:調用遠程對象一個方法時的租用更新時間,默認值為120秒;SponsorshipTimeout:超時值,通知Sponsor(發(fā)起人)租用過期后,Remoting會等待的時間,默認值為120秒;CurrentLeaseTime:當前租用時間,首次獲得租用時,為InitializeLeaseTime的值。</p><p> Remoting的遠程對象因為繼承了MarshalByR
132、efObject,因此默認繼承了InitializeLifetimeService方法,那么租用的相關屬性為默認值。如果要改變這些設置,可以在遠程對象中重寫該方法。例如: public override object InitializeLifetimeService() { ILease lease = (ILease)base.InitializeLifetimeService();
133、 if (lease.CurrentState == LeaseState.Initial) { lease.InitialLeaseTime = TimeSpan.FromMinutes(1); lease.RenewOnCallTime = TimeSpan.FromSeconds(20);
134、} return lease; }</p><p> 也可以忽略該方法,將對象的租用周期改變?yōu)闊o限: public override object InitializeLifetimeService() { return null; }</p><p><b>
135、; 2、租用管理器</b></p><p> 如果是前面所說的租用主要是應用在每個具體的遠程對象上,那么租用管理器是服務器端專門用來管理遠程對象生命周期的管理器,它維持著一個System.Hashtable成員,將租用映射為System.DateTime實例表示每個租用何時應過期。Remoting采用輪詢的方式以一定的時間喚醒租用管理器,檢查每個租用是否過期。默認為每10秒鐘喚醒一次。輪詢的間隔可
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 眾賞文庫僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
評論
0/150
提交評論