2023年全國碩士研究生考試考研英語一試題真題(含答案詳解+作文范文)_第1頁
已閱讀1頁,還剩95頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、<p>  Microsoft .Net Remoting系列專題之一</p><p>  一、Remoting基礎(chǔ)</p><p>  什么是Remoting,簡而言之,我們可以將其看作是一種分布式處理方式。從微軟的產(chǎn)品角度來看,可以說Remoting就是DCOM的一種升級,它改善了很多功能,并極好的融合到.Net平臺(tái)下。Microsoft® .NET Remotin

2、g 提供了一種允許對象通過應(yīng)用程序域與另一對象進(jìn)行交互的框架。這也正是我們使用Remoting的原因。為什么呢?在Windows操作系統(tǒng)中,是將應(yīng)用程序分離為單獨(dú)的進(jìn)程。這個(gè)進(jìn)程形成了應(yīng)用程序代碼和數(shù)據(jù)周圍的一道邊界。如果不采用進(jìn)程間通信(RPC)機(jī)制,則在一個(gè)進(jìn)程中執(zhí)行的代碼就不能訪問另一進(jìn)程。這是一種操作系統(tǒng)對應(yīng)用程序的保護(hù)機(jī)制。然而在某些情況下,我們需要跨過應(yīng)用程序域,與另外的應(yīng)用程序域進(jìn)行通信,即穿越邊界。</p>

3、<p>  在Remoting中是通過通道(channel)來實(shí)現(xiàn)兩個(gè)應(yīng)用程序域之間對象的通信的。如圖所示:</p><p>  首先,客戶端通過Remoting,訪問通道以獲得服務(wù)端對象,再通過代理解析為客戶端對象。這就提供一種可能性,即以服務(wù)的方式來發(fā)布服務(wù)器對象。遠(yuǎn)程對象代碼可以運(yùn)行在服務(wù)器上(如服務(wù)器激活的對象和客戶端激活的對象),然后客戶端再通過Remoting連接服務(wù)器,獲得該服務(wù)對象并通

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

5、;p>  Remoting的通道主要有兩種:Tcp和Http。在.Net中,System.Runtime.Remoting.Channel中定義了IChannel接口。IChannel接口包括了TcpChannel通道類型和Http通道類型。它們分別對應(yīng)Remoting通道的這兩種類型。</p><p>  TcpChannel類型放在名字空間System.Runtime.Remoting.Channel.

6、Tcp中。Tcp通道提供了基于Socket的傳輸工具,使用Tcp協(xié)議來跨越Remoting邊界傳輸序列化的消息流。TcpChannel類型默認(rèn)使用二進(jìn)制格式序列化消息對象,因此它具有更高的傳輸性能。HttpChannel類型放在名字空間System.Runtime.Remoting.Channel.Http中。它提供了一種使用Http協(xié)議,使其能在Internet上穿越防火墻傳輸序列化消息流。默認(rèn)情況下,HttpChannel類型使用S

7、oap格式序列化消息對象,因此它具有更好的互操作性。通常在局域網(wǎng)內(nèi),我們更多地使用TcpChannel;如果要穿越防火墻,則使用HttpChannel。</p><p>  2、遠(yuǎn)程對象的激活方式</p><p>  在訪問遠(yuǎn)程類型的一個(gè)對象實(shí)例之前,必須通過一個(gè)名為Activation的進(jìn)程創(chuàng)建它并進(jìn)行初始化。這種客戶端通過通道來創(chuàng)建遠(yuǎn)程對象,稱為對象的激活。在Remoting中,遠(yuǎn)程對

8、象的激活分為兩大類:服務(wù)器端激活和客戶端激活。</p><p>  (1) 服務(wù)器端激活,又叫做WellKnow方式,很多又翻譯為知名對象。為什么稱為知名對象激活模式呢?是因?yàn)榉?wù)器應(yīng)用程序在激活對象實(shí)例之前會(huì)在一個(gè)眾所周知的統(tǒng)一資源標(biāo)識符(URI)上來發(fā)布這個(gè)類型。然后該服務(wù)器進(jìn)程會(huì)為此類型配置一個(gè)WellKnown對象,并根據(jù)指定的端口或地址來發(fā)布對象。.Net Remoting把服務(wù)器端激活又分為Singl

9、eTon模式和SingleCall模式兩種。</p><p>  SingleTon模式:此為有狀態(tài)模式。如果設(shè)置為SingleTon激活方式,則Remoting將為所有客戶端建立同一個(gè)對象實(shí)例。當(dāng)對象處于活動(dòng)狀態(tài)時(shí),SingleTon實(shí)例會(huì)處理所有后來的客戶端訪問請求,而不管它們是同一個(gè)客戶端,還是其他客戶端。SingleTon實(shí)例將在方法調(diào)用中一直維持其狀態(tài)。舉例來說,如果一個(gè)遠(yuǎn)程對象有一個(gè)累加方法(i=0;

10、++i),被多個(gè)客戶端(例如兩個(gè))調(diào)用。如果設(shè)置為SingleTon方式,則第一個(gè)客戶獲得值為1,第二個(gè)客戶獲得值為2,因?yàn)樗麄儷@得的對象實(shí)例是相同的。如果熟悉Asp.Net的狀態(tài)管理,我們可以認(rèn)為它是一種Application狀態(tài)。</p><p>  SingleCall模式:SingleCall是一種無狀態(tài)模式。一旦設(shè)置為SingleCall模式,則當(dāng)客戶端調(diào)用遠(yuǎn)程對象的方法時(shí),Remoting會(huì)為每一個(gè)客

11、戶端建立一個(gè)遠(yuǎn)程對象實(shí)例,至于對象實(shí)例的銷毀則是由GC自動(dòng)管理的。同上一個(gè)例子而言,則訪問遠(yuǎn)程對象的兩個(gè)客戶獲得的都是1。我們?nèi)匀豢梢越梃bAsp.Net的狀態(tài)管理,認(rèn)為它是一種Session狀態(tài)。</p><p>  (2) 客戶端激活。與WellKnown模式不同,Remoting在激活每個(gè)對象實(shí)例的時(shí)候,會(huì)給每個(gè)客戶端激活的類型指派一個(gè)URI??蛻舳思せ钅J揭坏┇@得客戶端的請求,將為每一個(gè)客戶端都建立一個(gè)實(shí)例

12、引用。SingleCall模式和客戶端激活模式是有區(qū)別的:首先,對象實(shí)例創(chuàng)建的時(shí)間不一樣??蛻舳思せ罘绞绞强蛻粢坏┌l(fā)出調(diào)用的請求,就實(shí)例化;而SingleCall則是要等到調(diào)用對象方法時(shí)再創(chuàng)建。其次,SingleCall模式激活的對象是無狀態(tài)的,對象生命期的管理是由GC管理的,而客戶端激活的對象則有狀態(tài),其生命周期可自定義。其三,兩種激活模式在服務(wù)器端和客戶端實(shí)現(xiàn)的方法不一樣。尤其是在客戶端,SingleCall模式是由GetObjec

13、t()來激活,它調(diào)用對象默認(rèn)的構(gòu)造函數(shù)。而客戶端激活模式,則通過CreateInstance()來激活,它可以傳遞參數(shù),所以可以調(diào)用自定義的構(gòu)造函數(shù)來創(chuàng)建實(shí)例。</p><p><b>  二、遠(yuǎn)程對象的定義</b></p><p>  前面講到,客戶端在獲取服務(wù)器端對象時(shí),并不是獲得實(shí)際的服務(wù)端對象,而是獲得它的引用。因此在Remoting中,對于遠(yuǎn)程對象有一些必須

14、的定義規(guī)范要遵循。</p><p>  由于Remoting傳遞的對象是以引用的方式,因此所傳遞的遠(yuǎn)程對象類必須繼承MarshalByRefObject。MSDN對MarshalByRefObject的說明是:MarshalByRefObject 是那些通過使用代理交換消息來跨越應(yīng)用程序域邊界進(jìn)行通信的對象的基類。不是從 MarshalByRefObject 繼承的對象會(huì)以隱式方式按值封送。當(dāng)遠(yuǎn)程應(yīng)用程序引用一個(gè)

15、按值封送的對象時(shí),將跨越遠(yuǎn)程處理邊界傳遞該對象的副本。因?yàn)槟M褂么矸椒ǘ皇歉北痉椒ㄟM(jìn)行通信,因此需要繼承MarshallByRefObject。</p><p>  以下是一個(gè)遠(yuǎn)程對象類的定義: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>  這個(gè)類只實(shí)現(xiàn)了最簡單的方法,就是設(shè)置一個(gè)人的基本信息,并返回一個(gè)Person類對象。注意這里返回的Person類。由于這里所傳遞的P

19、erson則是以傳值的方式來完成的,而Remoting要求必須是引用的對象,所以必須將Person類序列化。</p><p>  因此,在Remoting中的遠(yuǎn)程對象中,如果還要調(diào)用或傳遞某個(gè)對象,例如類,或者結(jié)構(gòu),則該類或結(jié)構(gòu)則必須實(shí)現(xiàn)串行化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; }將該遠(yuǎn)程對象以類庫的方式編譯成Dll。這個(gè)Dll將分別放在服務(wù)器端和客戶端,以添加引用。</p><p>  在Remoting中能夠傳遞的遠(yuǎn)程對象可以是各種類型,包括復(fù)雜的DataSet對象,只要它能夠被序列化。遠(yuǎn)程對象也可以包含事件,但服務(wù)器端對于事件的處理比較特殊,我將在本系列之三中介紹。</p><p><b>  三、服務(wù)器端</b>

27、</p><p>  根據(jù)第一部分所述,根據(jù)激活模式的不同,通道類型的不同服務(wù)器端的實(shí)現(xiàn)方式也有所不同。大體上說,服務(wù)器端應(yīng)分為三步:</p><p><b>  1、注冊通道</b></p><p>  要跨越應(yīng)用程序域進(jìn)行通信,必須實(shí)現(xiàn)通道。如前所述,Remoting提供了IChannel接口,分別包含TcpChannel和HttpChan

28、nel兩種類型的通道。這兩種類型除了性能和序列化數(shù)據(jù)的格式不同外,實(shí)現(xiàn)的方式完全一致,因此下面我們就以TcpChannel為例。</p><p>  注冊TcpChannel,首先要在項(xiàng)目中添加引用“System.Runtime.Remoting”,然后using名字空間:System.Runtime.Remoting.Channel.Tcp。代碼如下:     

29、;       TcpChannel channel = new TcpChannel(8080);            ChannelServices.RegisterChannel(channel);</p><p>  在實(shí)例化通道對象

30、時(shí),將端口號作為參數(shù)傳遞。然后再調(diào)用靜態(tài)方法RegisterChannel()來注冊該通道對象即可。</p><p><b>  2、注冊遠(yuǎn)程對象</b></p><p>  注冊了通道后,要能激活遠(yuǎn)程對象,必須在通道中注冊該對象。根據(jù)激活模式的不同,注冊對象的方法也不同。</p><p>  (1) SingleTon模式</p>

31、<p>  對于WellKnown對象,可以通過靜態(tài)方法RemotingConfiguration.RegisterWellKnownServiceType()來實(shí)現(xiàn):RemotingConfiguration.RegisterWellKnownServiceType(             

32、;   typeof(ServerRemoteObject.ServerObject),                "ServiceMessage",WellKnownObjectMode.SingleTon);</p><

33、p>  (2)SingleCall模式</p><p>  注冊對象的方法基本上和SingleTon模式相同,只需要將枚舉參數(shù)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>  為什么要在注冊對象方法前設(shè)置ApplicationName屬性呢?其實(shí)這個(gè)屬性就是該對象的URI。對于WellKnown模式,U

37、RI是放在RegisterWellKnownServiceType()方法的參數(shù)中,當(dāng)然也可以拿出來專門對ApplicationName屬性賦值。而RegisterActivatedServiceType()方法的重載中,沒有ApplicationName的參數(shù),所以必須分開。</p><p><b>  3、注銷通道</b></p><p>  如果要關(guān)閉Remot

38、ing的服務(wù),則需要注銷通道,也可以關(guān)閉對通道的監(jiān)聽。在Remoting中當(dāng)我們注冊通道的時(shí)候,就自動(dòng)開啟了通道的監(jiān)聽。而如果關(guān)閉了對通道的監(jiān)聽,則該通道就無法接受客戶端的請求,但通道仍然存在,如果你想再一次注冊該通道,會(huì)拋出異常。</p><p>  //獲得當(dāng)前已注冊的通道;           

39、IChannel[] channels = ChannelServices.RegisteredChannels;</p><p>  //關(guān)閉指定名為MyTcp的通道;            foreach (IChannel eachChannel in channels)  

40、          {                if (eachChannel.ChannelName == "MyTcp")   

41、0;            {                    TcpChannel tcpChannel = (TcpChanne

42、l)eachChannel;</p><p>  //關(guān)閉監(jiān)聽;                    tcpChannel.StopListening(null);</p><p>  //注銷通道

43、;                    ChannelServices.UnregisterChannel(tcpChannel);         &#

44、160;      }            }代碼中,RegisterdChannel屬性獲得的是當(dāng)前已注冊的通道。在Remoting中,是允許同時(shí)注冊多個(gè)通道的,這一點(diǎn)會(huì)在后面說明。</p><p><b>  四、客戶端</b&g

45、t;</p><p>  客戶端主要做兩件事,一是注冊通道。這一點(diǎn)從圖一就可以看出,Remoting中服務(wù)器端和客戶端都必須通過通道來傳遞消息,以獲得遠(yuǎn)程對象。第二步則是獲得該遠(yuǎn)程對象。</p><p>  1、注冊通道:TcpChannel channel = new TcpChannel();ChannelServices.RegisterChannel(channel);<

46、/p><p>  注意在客戶端實(shí)例化通道時(shí),是調(diào)用的默認(rèn)構(gòu)造函數(shù),即沒有傳遞端口號。事實(shí)上,這個(gè)端口號是缺一不可的,只不過它的指定被放在后面作為了Uri的一部分。2、獲得遠(yuǎn)程對象。</p><p>  與服務(wù)器端相同,不同的激活模式?jīng)Q定了客戶端的實(shí)現(xiàn)方式也將不同。不過這個(gè)區(qū)別僅僅是WellKnown激活模式和客戶端激活模式之間的區(qū)別,而對于SingleTon和SingleCall模式,客戶

47、端的實(shí)現(xiàn)完全相同。</p><p>  (1) WellKnown激活模式</p><p>  要獲得服務(wù)器端的知名遠(yuǎn)程對象,可通過Activator進(jìn)程的GetObject()方法來獲得:ServerRemoteObject.ServerObject serverObj = (ServerRemoteObject.ServerObject)Activator.GetObject(&#

48、160;             typeof(ServerRemoteObject.ServerObject), "tcp://localhost:8080/ServiceMessage");</p><p>  首先以WellKnown模式激活,客戶端獲得對象的方法是使用

49、GetObject()。其中參數(shù)第一個(gè)是遠(yuǎn)程對象的類型。第二個(gè)參數(shù)就是服務(wù)器端的uri。如果是http通道,自然是用http://localhost:8080/ServiceMessage了。因?yàn)槲沂怯帽镜貦C(jī),所以這里是localhost,你可以用具體的服務(wù)器IP地址來代替它。端口必須和服務(wù)器端的端口一致。后面則是服務(wù)器定義的遠(yuǎn)程對象服務(wù)名,即ApplicationName屬性的內(nèi)容。(2) 客戶端激活模式</p>&

50、lt;p>  如前所述,WellKnown模式在客戶端創(chuàng)建對象時(shí),只能調(diào)用默認(rèn)的構(gòu)造函數(shù),上面的代碼就說明了這一點(diǎn),因?yàn)镚etObject()方法不能傳遞構(gòu)造函數(shù)的參數(shù)。而客戶端激活模式則可以通過自定義的構(gòu)造函數(shù)來創(chuàng)建遠(yuǎn)程對象。</p><p>  客戶端激活模式有兩種方法:1) 調(diào)用RemotingConfiguration的靜態(tài)方法RegisterActivatedClientType()。這個(gè)方法返

51、回值為Void,它只是將遠(yuǎn)程對象注冊在客戶端而已。具體的實(shí)例化還需要調(diào)用對象類的構(gòu)造函數(shù)。 RemotingConfiguration.RegisterActivatedClientType(                   

52、60;           typeof(ServerRemoteObject.ServerObject),                "tcp://localhost:80

53、80/ServiceMessage"); ServerRemoteObject.ServerObject serverObj = new ServerRemoteObject.ServerObject();</p><p>  2) 調(diào)用進(jìn)程Activator的CreateInstance()方法。這個(gè)方法將創(chuàng)建方法參數(shù)指定類型的類對象。它與前面的GetObject()不同的是,它要在客戶端

54、調(diào)用構(gòu)造函數(shù),而GetObject()只是獲得對象,而創(chuàng)建實(shí)例是在服務(wù)器端完成的。CreateInstance()方法有很多個(gè)重載,我著重說一下其中常用的兩個(gè)。a、 public static object CreateInstance(Type type, object[] args, object[] activationAttributes);</p><p>  參數(shù)說明:type:要?jiǎng)?chuàng)建的

55、對象的類型。args :與要調(diào)用構(gòu)造函數(shù)的參數(shù)數(shù)量、順序和類型匹配的參數(shù)數(shù)組。如果 args 為空數(shù)組或空引用(Visual Basic 中為 Nothing),則調(diào)用不帶任何參數(shù)的構(gòu)造函數(shù)(默認(rèn)構(gòu)造函數(shù))。activationAttributes :包含一個(gè)或多個(gè)可以參與激活的屬性的數(shù)組。</p><p>  這里的參數(shù)args是一個(gè)object[]數(shù)組類型。它可以傳遞要?jiǎng)?chuàng)建對象的構(gòu)造函數(shù)中的參數(shù)。從這里其

56、實(shí)可以得到一個(gè)結(jié)論:WellKnown激活模式所傳遞的遠(yuǎn)程對象類,只能使用默認(rèn)的構(gòu)造函數(shù);而Activated模式則可以用戶自定義構(gòu)造函數(shù)。activationAttributes參數(shù)在這個(gè)方法中通常用來傳遞服務(wù)器的url。假設(shè)我們的遠(yuǎn)程對象類ServerObject有個(gè)構(gòu)造函數(shù):            ServerObje

57、ct(string pName,string pSex,int pAge)            {                name = pName; &#

58、160;              sex = pSex;                age = pAge;   

59、0;        }</p><p>  那么實(shí)現(xiàn)的代碼是:            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[]數(shù)組傳遞的就是構(gòu)造函數(shù)的參數(shù)。</p><p>  b、public static ObjectHandle CreateInstance(string assemblyName, string typeName, object[] activationAttribute);</p><p>  參數(shù)說明:assemblyName :將在其中查找名為 typeName 的類

64、型的程序集的名稱。如果 assemblyName 為空引用(Visual Basic 中為 Nothing),則搜索正在執(zhí)行的程序集。typeName:首選類型的名稱。activationAttributes :包含一個(gè)或多個(gè)可以參與激活的屬性的數(shù)組。</p><p>  參數(shù)說明一目了然。注意這個(gè)方法返回值為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>  這個(gè)方法實(shí)際上是調(diào)用的默認(rèn)構(gòu)造函數(shù)。ObjectHandle.Unwrap()方法是返回被包裝的對象。</p><p>  說明:要使用UrlAttribute,還需要在命名空間中添加:usin

69、g System.Runtime.Remoting.Activation;</p><p>  五、Remoting基礎(chǔ)的補(bǔ)充</p><p>  通過上面的描述,基本上已經(jīng)完成了一個(gè)最簡單的Remoting程序。這是一個(gè)標(biāo)準(zhǔn)的創(chuàng)建Remoting程序的方法,但在實(shí)際開發(fā)過程中,我們遇到的情況也許千奇百怪,如果只掌握一種所謂的“標(biāo)準(zhǔn)”,就妄想可以“一招鮮、吃遍天”,是不可能的。</p

70、><p><b>  1、注冊多個(gè)通道</b></p><p>  在Remoting中,允許同時(shí)創(chuàng)建多個(gè)通道,即根據(jù)不同的端口創(chuàng)建不同的通道。但是,Remoting要求通道的名字必須不同,因?yàn)樗脕碜鳛橥ǖ赖奈ㄒ粯?biāo)識符。雖然IChannel有ChannelName屬性,但這個(gè)屬性是只讀的。因此前面所述的創(chuàng)建通道的方法無法實(shí)現(xiàn)同時(shí)注冊多個(gè)通道的要求。</p>

71、<p>  這個(gè)時(shí)候,我們必須用到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、遠(yuǎn)程對象元數(shù)據(jù)相關(guān)性</p><p>  由于服務(wù)器端和客戶端都要用到遠(yuǎn)程對象,通常的方式是生成兩份完全相同的對象Dll,分別添加引用。不過為

75、了代碼的安全性,且降低客戶端對遠(yuǎn)程對象元數(shù)據(jù)的相關(guān)性,我們有必要對這種方式進(jìn)行改動(dòng)。即在服務(wù)器端實(shí)現(xiàn)遠(yuǎn)程對象,而在客戶端則刪除這些實(shí)現(xiàn)的元數(shù)據(jù)。</p><p>  由于激活模式的不同,在客戶端創(chuàng)建對象的方法也不同,所以要分離元數(shù)據(jù)的相關(guān)性,也應(yīng)分為兩種情況。</p><p>  (1) WellKnown激活模式:</p><p>  通過接口來實(shí)現(xiàn)。在服務(wù)器端,

76、提供接口和具體類的實(shí)現(xiàn),而在客戶端僅提供接口:    public interface IServerObject    {        Person GetPersonInfo(string name,string sex,int age);    }</p&g

77、t;<p>  public class ServerObject:MarshalByRefObject,IServerObject{ ......}注意:兩邊生成該對象程序集的名字必須相同,嚴(yán)格地說,是命名空間的名字必須相同。           (2) 客戶端激活模式:</p><p

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

79、的“抽象工廠”,下面的類圖表描述了總體解決方案:</p><p>  我們在服務(wù)器端的遠(yuǎn)程對象中加上抽象工廠的接口和實(shí)現(xiàn)類:    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>  然后再客戶端的遠(yuǎn)程對象中只提供工廠接口和原來的對象接口:    public interface ISe

87、rverObject    {        Person GetPersonInfo(string name,string sex,int age);    }</p><p>  public interface IServerObjFactory  &

88、#160; {        IServerObject CreateInstance();           }我們用WellKnown激活模式注冊遠(yuǎn)程對象,在服務(wù)器端:       &#

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模式來激活對象,但此時(shí)激活的并非我們要傳遞的遠(yuǎn)程對象,而是工廠對象。如果客戶端要?jiǎng)?chuàng)建遠(yuǎn)程對象,還應(yīng)

95、該通過工廠對象的CreateInstance()方法來獲得。而這個(gè)方法正是在客戶端調(diào)用的。因此它的實(shí)現(xiàn)方式就等同于客戶端激活模式。</p><p>  b、利用替代類來取代遠(yuǎn)程對象的元數(shù)據(jù)</p><p>  實(shí)際上,我們可以用一個(gè)trick,來欺騙Remoting。這里所說的替代類就是這個(gè)trick了。既然是提供服務(wù),Remoting傳遞的遠(yuǎn)程對象其實(shí)現(xiàn)的細(xì)節(jié)當(dāng)然是放在服務(wù)器端。而要在客

96、戶端放對象的副本,不過是因?yàn)榭蛻舳吮仨氄{(diào)用構(gòu)造函數(shù),而采取的無奈之舉。既然具體的實(shí)現(xiàn)是在服務(wù)器端,又為了能在客戶端實(shí)例化,那么在客戶端就實(shí)現(xiàn)這些好了。至于實(shí)現(xiàn)的細(xì)節(jié),就不用管了。</p><p>  如果遠(yuǎn)程對象有方法,服務(wù)器端則提供方法實(shí)現(xiàn),而客戶端就提供這個(gè)方法就OK了,至于里面的實(shí)現(xiàn),你可以是拋出一個(gè)異常,或者return 一個(gè)null值;如果方法返回void,那么里面可以是空。關(guān)鍵是這個(gè)客戶端類對象要有這

97、個(gè)方法。這個(gè)方法的實(shí)現(xiàn),其實(shí)和方法的聲明差不多,所以我說是一個(gè)trick。方法如是,構(gòu)造函數(shù)也如此。</p><p>  還是用代碼來說明這種“陰謀”,更直觀:</p><p>  服務(wù)器端:    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>  比較客戶端和服務(wù)器端,客戶端的方法GetPersonInfo(),沒有具體的實(shí)現(xiàn)細(xì)節(jié),只是拋出了一個(gè)異常?;蛘咧苯訉懮险Z句return null,照樣OK。我們稱客戶端的這個(gè)類為遠(yuǎn)程對象的替代類。</p><p>  3、利用配置文件實(shí)現(xiàn)</p&

106、gt;<p>  前面所述的方法,于服務(wù)器uri、端口、以及激活模式的設(shè)置是用代碼來完成的。其實(shí)我們也可以用配置文件來設(shè)置。這樣做有個(gè)好處,因?yàn)檫@個(gè)配置文件是Xml文檔。如果需要改變端口或其他,我們就不需要修改程序,并重新編譯,而是只需要改變這個(gè)配置文件即可。</p><p>  (1) 服務(wù)器端的配置文件:&lt;configuration&gt;  &lt;

107、system.runtime.remoting&gt;    &lt;application name="ServerRemoting"&gt;      &lt;service&gt;        &lt;wel

108、lknown mode="Singleton" type="ServerRemoteObject.ServerObject" objectUri="ServiceMessage"/&gt;      &lt;/service&gt;      &

109、;lt;channels&gt;         &lt;channel ref="tcp" port="8080"/&gt;      &lt;/channels&gt;    &lt

110、;/application&gt;  &lt;/system.runtime.remoting&gt;&lt;/configuration&gt;</p><p>  如果是客戶端激活模式,則把wellknown改為activated,同時(shí)刪除mode屬性。</p><p>  把該配置文件放到服務(wù)器程序的應(yīng)用程序文件夾中,命名為Ser

111、verRemoting.config。那么前面的服務(wù)器端程序直接用這條語句即可:RemotingConfiguration.Configure("ServerRemoting.config");</p><p>  (2) 客戶端配置文件</p><p>  如果是客戶端激活模式,修改和上面一樣。調(diào)用也是使用RemotingConfiguration.Configur

112、e()方法來調(diào)用存儲(chǔ)在客戶端的配置文件。</p><p>  配置文件還可以放在machine.config中。如果客戶端程序是web應(yīng)用程序,則可以放在web.config中。</p><p>  4、啟動(dòng)/關(guān)閉指定遠(yuǎn)程對象</p><p>  Remoting中沒有提供類似UnregisterWellKnownServiceType()的方法,也即是說,一旦通過

113、注冊了遠(yuǎn)程對象,如果沒有關(guān)閉通道的話,該對象就一直存在于通道中。只要客戶端激活該對象,就會(huì)創(chuàng)建對象實(shí)例。如果Remoting傳送的只有一個(gè)遠(yuǎn)程對象,這不存在問題,關(guān)閉通道就可以了。如果傳送多個(gè)遠(yuǎn)程對象呢?要關(guān)閉指定的遠(yuǎn)程對象應(yīng)該怎么做?關(guān)閉之后又需要啟動(dòng)又該如何?</p><p>  我們注意到在Remoting中提供了Marshal()和Disconnect()方法,答案就在這里。Marshal()方法是將Ma

114、rshalByRefObject類對象轉(zhuǎn)化為ObjRef類對象,這個(gè)對象是存儲(chǔ)生成代理以與遠(yuǎn)程對象通訊所需的所有相關(guān)信息。這樣就可以將該實(shí)例序列化以便在應(yīng)用程序域之間以及通過網(wǎng)絡(luò)進(jìn)行傳輸,客戶端就可以調(diào)用了。而Disconnect()方法則將具體的實(shí)例對象從通道中斷開。</p><p>  方法如下:首先注冊通道:TcpChannel channel = new TcpChannel(8080);Chann

115、elServices.RegisterChannel(channel);</p><p>  接著啟動(dòng)服務(wù):先在服務(wù)器端實(shí)例化遠(yuǎn)程對象。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的類對象必須是前面實(shí)例化的對象。正因?yàn)榇?,我們可以根?jù)需要?jiǎng)?chuàng)建指定的遠(yuǎn)程對象,而關(guān)閉時(shí),則Disconnect之前實(shí)例化的對象。</p><p>  至于客戶端的調(diào)用,和前面WellKnown模式的方法相同,仍然是通過Activator.GetObject()來獲得。但從實(shí)現(xiàn)代碼來看,我們會(huì)注意到一個(gè)問題,由于服務(wù)器端是顯式的實(shí)例化了遠(yuǎn)程對象,因此不管客戶端有多少,是否相同,它們調(diào)用的

118、都是同一個(gè)遠(yuǎn)程對象。因此我們將這個(gè)方法稱為模擬的SingleTon模式。</p><p><b>  客戶端激活模式</b></p><p>  我們也可以通過Marshal()和Disconnect()來模擬客戶端激活模式。首先我們來回顧“遠(yuǎn)程對象元數(shù)據(jù)相關(guān)性”一節(jié),在這一節(jié)中,我說到采用設(shè)計(jì)模式的“抽象工廠”來創(chuàng)建對象實(shí)例,以此用SingleCall模式來模擬客戶

119、端激活模式。在仔細(xì)想想前面的模擬的SingleTon模式。是不是答案就將呼之欲出呢?</p><p>  在“模擬的SingleTon”模式中,我們是將具體的遠(yuǎn)程對象實(shí)例進(jìn)行Marshal,以此讓客戶端獲得該對象的引用信息。那么我們換一種思路,當(dāng)我們用抽象工廠提供接口,工廠類實(shí)現(xiàn)創(chuàng)建遠(yuǎn)程對象的方法。然后我們在服務(wù)器端創(chuàng)建工廠類實(shí)例。再將這個(gè)工廠類實(shí)例進(jìn)行Marshal。而客戶端獲取對象時(shí),不是獲取具體的遠(yuǎn)程對象,

120、而是獲取具體的工廠類對象。然后再調(diào)用CreateInstance()方法來創(chuàng)建具體的遠(yuǎn)程對象實(shí)例。此時(shí),對于多個(gè)客戶端而言,調(diào)用的是同一個(gè)工廠類對象;然而遠(yuǎn)程對象是在各個(gè)客戶端自己創(chuàng)建的,因此對于遠(yuǎn)程對象而言,則是由客戶端激活,創(chuàng)建的是不同對象了。</p><p>  當(dāng)我們要啟動(dòng)/關(guān)閉指定對象時(shí),只需要用Disconnet()方法來注銷工廠類對象就可以了。</p><p><b&g

121、t;  六、小結(jié)</b></p><p>  Microsoft.Net Remoting真可以說是博大精深。整個(gè)Remoting的內(nèi)容不是我這一篇小文所能盡述的,更不是我這個(gè)Remoting的初學(xué)者所能掌握的。王國維在《人間詞話》一書中寫到:古今之成大事業(yè)大學(xué)問者,必經(jīng)過三種境界?!白蛞刮黠L(fēng)凋碧樹,獨(dú)上高樓,望盡天涯路。”此第一境界也。“衣帶漸寬終不悔,為伊消得人憔悴?!贝说诙辰缫病!氨娎飳にО?/p>

122、度,驀然回首,那人卻在燈火闌珊處?!贝说谌辰缫病H缫源藖硇稳菸覍emoting的學(xué)習(xí),還處于“獨(dú)上高樓,望盡天涯路”的時(shí)候,真可以說還未曾登堂入室。</p><p>  或許需得“衣帶漸寬”,學(xué)得Remoting“終不悔”,方才可以“驀然回首”吧。</p><p>  posted on 2004-07-30 20:44 張逸 閱讀(69253) 評論(152)  編輯 收藏

123、 網(wǎng)摘 所屬分類: .NET Remoting</p><p>  Microsoft .Net Remoting系列專題之二:Marshal、Disconnect與生命周期以及跟蹤服務(wù) </p><p>  我寫的.Net Remoting系列專題:</p><p>  Microsoft .Net Remoting系列專題之一:.Net Remoting基礎(chǔ)篇&

124、lt;/p><p>  Microsoft .Net Remoting系列專題之三:Remoting事件處理全接觸</p><p>  Microsoft .Net Remoting系列專題之二 </p><p><b>  一、遠(yuǎn)程對象的激活</b></p><p>  在Remoting中有三種激活方式,一般的

125、實(shí)現(xiàn)是通過RemotingServices類的靜態(tài)方法來完成。工作過程事實(shí)上是將該遠(yuǎn)程對象注冊到通道中。由于Remoting沒有提供與之對應(yīng)的Unregister方法來注銷遠(yuǎn)程對象,所以如果需要注冊/注銷指定對象,微軟推薦使用Marshal(一般譯為編組)和Disconnect配對使用。在《Net Remoting基礎(chǔ)篇》中我已經(jīng)談到:Marshal()方法是將MarshalByRefObject類對象轉(zhuǎn)化為ObjRef類對象,這個(gè)對象

126、是存儲(chǔ)生成代理以與遠(yuǎn)程對象通訊所需的所有相關(guān)信息。這樣就可以將該實(shí)例序列化以便在應(yīng)用程序域之間以及通過網(wǎng)絡(luò)進(jìn)行傳輸,客戶端就可以調(diào)用了。而Disconnect()方法則將具體的實(shí)例對象從通道中斷開。</p><p>  根據(jù)上述說明,Marshal()方法對遠(yuǎn)程對象以引用方式進(jìn)行編組(Marshal-by-Reference,MBR),并將對象的代理信息放到通道中??蛻舳丝梢酝ㄟ^Activator.GetObje

127、ct()來獲取。如果用戶要注銷該對象,則通過調(diào)用Disconnect()方法。那么這種方式對于編組的遠(yuǎn)程對象是否存在生命周期的管理呢?這就是本文所要描述的問題。</p><p><b>  二、生命周期</b></p><p>  在CLR中,框架提供了GC(垃圾回收器)來管理內(nèi)存中對象的生命周期。同樣的,.Net Remoting使用了一種分布式垃圾回收,基于租用的

128、形式來管理遠(yuǎn)程對象的生命周期。</p><p>  早期的DCOM對于對象生命周期的管理是通過ping和引用計(jì)數(shù)來確定對象何時(shí)應(yīng)當(dāng)作為垃圾回收。然而ping引起的網(wǎng)絡(luò)流量對分布式應(yīng)用程序的性能是一種痛苦的負(fù)擔(dān),它大大地影響了分布式處理的整體性能。.Net Remoting在每個(gè)應(yīng)用程序域中都引入一個(gè)租用管理器,為每個(gè)服務(wù)器端的SingleTon,或每個(gè)客戶端激活的遠(yuǎn)程對象保存著對租用對象的引用。(說明:對于服務(wù)器

129、端激活的SingleCall方式,由于它是無狀態(tài)的,對于每個(gè)激活的遠(yuǎn)程對象,都由CLR的GC來自動(dòng)回收,因此對于SingleCall模式激活的遠(yuǎn)程對象,不存在生命周期的管理。)</p><p><b>  1、租用</b></p><p>  租用是個(gè)封裝了TimeSpan值的對象,用以管理遠(yuǎn)程對象的生存期。在.Net Remoting中提供了定義租用功能的ILeas

130、e接口。當(dāng)Remoting通過SingleTon模式或客戶端激活模式來激活遠(yuǎn)程對象時(shí),租用對象調(diào)用從System.MarshalByRefObject繼承的InitializeLifetimeService方法,向?qū)ο笳埱笞庥谩?lt;/p><p>  ILease接口定義了有關(guān)生命周期的屬性,均為TimeSpan值。如下:InitialLeaseTime:初始化有效時(shí)間,默認(rèn)值為300秒,如果為0,表示永不過期;

131、RenewOnCallTime:調(diào)用遠(yuǎn)程對象一個(gè)方法時(shí)的租用更新時(shí)間,默認(rèn)值為120秒;SponsorshipTimeout:超時(shí)值,通知Sponsor(發(fā)起人)租用過期后,Remoting會(huì)等待的時(shí)間,默認(rèn)值為120秒;CurrentLeaseTime:當(dāng)前租用時(shí)間,首次獲得租用時(shí),為InitializeLeaseTime的值。</p><p>  Remoting的遠(yuǎn)程對象因?yàn)槔^承了MarshalByR

132、efObject,因此默認(rèn)繼承了InitializeLifetimeService方法,那么租用的相關(guān)屬性為默認(rèn)值。如果要改變這些設(shè)置,可以在遠(yuǎn)程對象中重寫該方法。例如: 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>  如果是前面所說的租用主要是應(yīng)用在每個(gè)具體的遠(yuǎn)程對象上,那么租用管理器是服務(wù)器端專門用來管理遠(yuǎn)程對象生命周期的管理器,它維持著一個(gè)System.Hashtable成員,將租用映射為System.DateTime實(shí)例表示每個(gè)租用何時(shí)應(yīng)過期。Remoting采用輪詢的方式以一定的時(shí)間喚醒租用管理器,檢查每個(gè)租用是否過期。默認(rèn)為每10秒鐘喚醒一次。輪詢的間隔可

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 眾賞文庫僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論