Microsoft.NetRemoting基礎(chǔ)的示例分析-創(chuàng)新互聯(lián)

這篇文章主要為大家展示了“Microsoft .Net Remoting基礎(chǔ)的示例分析”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Microsoft .Net Remoting基礎(chǔ)的示例分析”這篇文章吧。

站在用戶(hù)的角度思考問(wèn)題,與客戶(hù)深入溝通,找到柳江網(wǎng)站設(shè)計(jì)與柳江網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶(hù)體驗(yàn)好的作品,建站類(lèi)型包括:做網(wǎng)站、成都做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名注冊(cè)、虛擬空間、企業(yè)郵箱。業(yè)務(wù)覆蓋柳江地區(qū)。

一、Remoting基礎(chǔ)

什么是Remoting,簡(jiǎn)而言之,我們可以將其看作是一種分布式處理方式。從微軟的產(chǎn)品角度來(lái)看,可以說(shuō)Remoting就是DCOM的一種升級(jí),它改善了很多功能,并好的融合到.Net平臺(tái)下。Microsoft .NET Remoting 提供了一種允許對(duì)象通過(guò)應(yīng)用程序域與另一對(duì)象進(jìn)行交互的框架。這也正是我們使用Remoting的原因。為什么呢?在Windows操作系統(tǒng)中,是將應(yīng)用程序分離為單獨(dú)的進(jìn)程。這個(gè)進(jìn)程形成了應(yīng)用程序代碼和數(shù)據(jù)周?chē)囊坏肋吔纭H绻徊捎眠M(jìn)程間通信(RPC)機(jī)制,則在一個(gè)進(jìn)程中執(zhí)行的代碼就不能訪(fǎng)問(wèn)另一進(jìn)程。這是一種操作系統(tǒng)對(duì)應(yīng)用程序的保護(hù)機(jī)制。然而在某些情況下,我們需要跨過(guò)應(yīng)用程序域,與另外的應(yīng)用程序域進(jìn)行通信,即穿越邊界。

在Remoting中是通過(guò)通道(channel)來(lái)實(shí)現(xiàn)兩個(gè)應(yīng)用程序域之間對(duì)象的通信的。如圖所示:

Microsoft .Net Remoting基礎(chǔ)的示例分析

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

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

1、Remoting的兩種通道

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

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

2、遠(yuǎn)程對(duì)象的激活方式

在訪(fǎng)問(wèn)遠(yuǎn)程類(lèi)型的一個(gè)對(duì)象實(shí)例之前,必須通過(guò)一個(gè)名為Activation的進(jìn)程創(chuàng)建它并進(jìn)行初始化。這種客戶(hù)端通過(guò)通道來(lái)創(chuàng)建遠(yuǎn)程對(duì)象,稱(chēng)為對(duì)象的激活。在Remoting中,遠(yuǎn)程對(duì)象的激活分為兩大類(lèi):服務(wù)器端激活和客戶(hù)端激活。

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

SingleTon模式:此為有狀態(tài)模式。如果設(shè)置為SingleTon激活方式,則Remoting將為所有客戶(hù)端建立同一個(gè)對(duì)象實(shí)例。當(dāng)對(duì)象處于活動(dòng)狀態(tài)時(shí),SingleTon實(shí)例會(huì)處理所有后來(lái)的客戶(hù)端訪(fǎng)問(wèn)請(qǐng)求,而不管它們是同一個(gè)客戶(hù)端,還是其他客戶(hù)端。SingleTon實(shí)例將在方法調(diào)用中一直維持其狀態(tài)。舉例來(lái)說(shuō),如果一個(gè)遠(yuǎn)程對(duì)象有一個(gè)累加方法(i=0;++i),被多個(gè)客戶(hù)端(例如兩個(gè))調(diào)用。如果設(shè)置為SingleTon方式,則第一個(gè)客戶(hù)獲得值為1,第二個(gè)客戶(hù)獲得值為2,因?yàn)樗麄儷@得的對(duì)象實(shí)例是相同的。如果熟悉Asp.Net的狀態(tài)管理,我們可以認(rèn)為它是一種Application狀態(tài)。

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

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

二、遠(yuǎn)程對(duì)象的定義

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

由于Remoting傳遞的對(duì)象是以引用的方式,因此所傳遞的遠(yuǎn)程對(duì)象類(lèi)必須繼承MarshalByRefObject。MSDN對(duì)MarshalByRefObject的說(shuō)明是:MarshalByRefObject 是那些通過(guò)使用代理交換消息來(lái)跨越應(yīng)用程序域邊界進(jìn)行通信的對(duì)象的基類(lèi)。不是從 MarshalByRefObject 繼承的對(duì)象會(huì)以隱式方式按值封送。當(dāng)遠(yuǎn)程應(yīng)用程序引用一個(gè)按值封送的對(duì)象時(shí),將跨越遠(yuǎn)程處理邊界傳遞該對(duì)象的副本。因?yàn)槟M褂么矸椒ǘ皇歉北痉椒ㄟM(jìn)行通信,因此需要繼承MarshallByRefObject。

以下是一個(gè)遠(yuǎn)程對(duì)象類(lèi)的定義:

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;
 }
}

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

因此,在Remoting中的遠(yuǎn)程對(duì)象中,如果還要調(diào)用或傳遞某個(gè)對(duì)象,例如類(lèi),或者結(jié)構(gòu),則該類(lèi)或結(jié)構(gòu)則必須實(shí)現(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;}
 }
}

將該遠(yuǎn)程對(duì)象以類(lèi)庫(kù)的方式編譯成Dll。這個(gè)Dll將分別放在服務(wù)器端和客戶(hù)端,以添加引用。

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

三、服務(wù)器端

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

1、注冊(cè)通道

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

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

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

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

2、注冊(cè)遠(yuǎn)程對(duì)象

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

(1) SingleTon模式

對(duì)于WellKnown對(duì)象,可以通過(guò)靜態(tài)方法RemotingConfiguration.RegisterWellKnownServiceType()來(lái)實(shí)現(xiàn):

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

(2)SingleCall模式

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

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

(3)客戶(hù)端激活模式

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

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

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

3、注銷(xiāo)通道

如果要關(guān)閉Remoting的服務(wù),則需要注銷(xiāo)通道,也可以關(guān)閉對(duì)通道的監(jiān)聽(tīng)。在Remoting中當(dāng)我們注冊(cè)通道的時(shí)候,就自動(dòng)開(kāi)啟了通道的監(jiān)聽(tīng)。而如果關(guān)閉了對(duì)通道的監(jiān)聽(tīng),則該通道就無(wú)法接受客戶(hù)端的請(qǐng)求,但通道仍然存在,如果你想再一次注冊(cè)該通道,會(huì)拋出異常。

//獲得當(dāng)前已注冊(cè)的通道;
IChannel[] channels = ChannelServices.RegisteredChannels;

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

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

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

代碼中,RegisterdChannel屬性獲得的是當(dāng)前已注冊(cè)的通道。在Remoting中,是允許同時(shí)注冊(cè)多個(gè)通道的,這一點(diǎn)會(huì)在后面說(shuō)明。

四、客戶(hù)端

客戶(hù)端主要做兩件事,一是注冊(cè)通道。這一點(diǎn)從圖一就可以看出,Remoting中服務(wù)器端和客戶(hù)端都必須通過(guò)通道來(lái)傳遞消息,以獲得遠(yuǎn)程對(duì)象。第二步則是獲得該遠(yuǎn)程對(duì)象。

1、注冊(cè)通道:

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

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

2、獲得遠(yuǎn)程對(duì)象。

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

(1) WellKnown激活模式

要獲得服務(wù)器端的知名遠(yuǎn)程對(duì)象,可通過(guò)Activator進(jìn)程的GetObject()方法來(lái)獲得:

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

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

(2) 客戶(hù)端激活模式

如前所述,WellKnown模式在客戶(hù)端創(chuàng)建對(duì)象時(shí),只能調(diào)用默認(rèn)的構(gòu)造函數(shù),上面的代碼就說(shuō)明了這一點(diǎn),因?yàn)镚etObject()方法不能傳遞構(gòu)造函數(shù)的參數(shù)。而客戶(hù)端激活模式則可以通過(guò)自定義的構(gòu)造函數(shù)來(lái)創(chuàng)建遠(yuǎn)程對(duì)象。

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

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

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

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

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

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


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

那么實(shí)現(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ù)說(shuō)明:
assemblyName :將在其中查找名為 typeName 的類(lèi)型的程序集的名稱(chēng)。如果 assemblyName 為空引用(Visual Basic 中為 Nothing),則搜索正在執(zhí)行的程序集。
typeName:選類(lèi)型的名稱(chēng)。
activationAttributes :包含一個(gè)或多個(gè)可以參與激活的屬性的數(shù)組。

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

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

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

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

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

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

1、注冊(cè)多個(gè)通道

在Remoting中,允許同時(shí)創(chuàng)建多個(gè)通道,即根據(jù)不同的端口創(chuàng)建不同的通道。但是,Remoting要求通道的名字必須不同,因?yàn)樗脕?lái)作為通道的標(biāo)識(shí)符。雖然IChannel有ChannelName屬性,但這個(gè)屬性是只讀的。因此前面所述的創(chuàng)建通道的方法無(wú)法實(shí)現(xiàn)同時(shí)注冊(cè)多個(gè)通道的要求。

這個(gè)時(shí)候,我們必須用到System.Collection中的IDictionary接口:

注冊(cè)Tcp通道:

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

注冊(cè)Http通道:

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

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

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

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

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

(1) WellKnown激活模式:

通過(guò)接口來(lái)實(shí)現(xiàn)。在服務(wù)器端,提供接口和具體類(lèi)的實(shí)現(xiàn),而在客戶(hù)端僅提供接口:

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

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

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

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

a、利用WellKnown激活模式模擬客戶(hù)端激活模式:

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

Microsoft .Net Remoting基礎(chǔ)的示例分析

我們?cè)诜?wù)器端的遠(yuǎn)程對(duì)象中加上抽象工廠(chǎng)的接口和實(shí)現(xiàn)類(lèi):

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();
 }
}

然后再客戶(hù)端的遠(yuǎn)程對(duì)象中只提供工廠(chǎng)接口和原來(lái)的對(duì)象接口:

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

public interface IServerObjFactory
{
 IServerObject CreateInstance();  
}

我們用WellKnown激活模式注冊(cè)遠(yuǎn)程對(duì)象,在服務(wù)器端:

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

注意這里注冊(cè)的不是ServerObject類(lèi)對(duì)象,而是ServerObjFactory類(lèi)對(duì)象。

客戶(hù)端:

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

ServerRemoteObject.IServerObject serverObj = serverFactory.CreateInstance();

為什么說(shuō)這是一種客戶(hù)端激活模式的模擬呢?從激活的方法來(lái)看,我們是使用了SingleCall模式來(lái)激活對(duì)象,但此時(shí)激活的并非我們要傳遞的遠(yuǎn)程對(duì)象,而是工廠(chǎng)對(duì)象。如果客戶(hù)端要?jiǎng)?chuàng)建遠(yuǎn)程對(duì)象,還應(yīng)該通過(guò)工廠(chǎng)對(duì)象的CreateInstance()方法來(lái)獲得。而這個(gè)方法正是在客戶(hù)端調(diào)用的。因此它的實(shí)現(xiàn)方式就等同于客戶(hù)端激活模式。

b、利用替代類(lèi)來(lái)取代遠(yuǎn)程對(duì)象的元數(shù)據(jù)

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

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

還是用代碼來(lái)說(shuō)明這種“陰謀”,更直觀(guān):

服務(wù)器端:

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;
 }  
}

客戶(hù)端:

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

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

比較客戶(hù)端和服務(wù)器端,客戶(hù)端的方法GetPersonInfo(),沒(méi)有具體的實(shí)現(xiàn)細(xì)節(jié),只是拋出了一個(gè)異常。或者直接寫(xiě)上語(yǔ)句return null,照樣OK。我們稱(chēng)客戶(hù)端的這個(gè)類(lèi)為遠(yuǎn)程對(duì)象的替代類(lèi)。

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

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

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

<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>

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

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

RemotingConfiguration.Configure("ServerRemoting.config");

(2) 客戶(hù)端配置文件

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

配置文件還可以放在machine.config中。如果客戶(hù)端程序是web應(yīng)用程序,則可以放在web.config中。

4、啟動(dòng)/關(guān)閉指定遠(yuǎn)程對(duì)象

Remoting中沒(méi)有提供類(lèi)似UnregisterWellKnownServiceType()的方法,也即是說(shuō),一旦通過(guò)注冊(cè)了遠(yuǎn)程對(duì)象,如果沒(méi)有關(guān)閉通道的話(huà),該對(duì)象就一直存在于通道中。只要客戶(hù)端激活該對(duì)象,就會(huì)創(chuàng)建對(duì)象實(shí)例。如果Remoting傳送的只有一個(gè)遠(yuǎn)程對(duì)象,這不存在問(wèn)題,關(guān)閉通道就可以了。如果傳送多個(gè)遠(yuǎn)程對(duì)象呢?要關(guān)閉指定的遠(yuǎn)程對(duì)象應(yīng)該怎么做?關(guān)閉之后又需要啟動(dòng)又該如何?

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

方法如下:
首先注冊(cè)通道:

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

接著啟動(dòng)服務(wù):
先在服務(wù)器端實(shí)例化遠(yuǎn)程對(duì)象。

ServerObject obj = new ServerObject();

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

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

如果要注銷(xiāo)對(duì)象,則:

RemotingServices.Disconnect(obj);

要注意,這里Disconnect的類(lèi)對(duì)象必須是前面實(shí)例化的對(duì)象。正因?yàn)榇耍覀兛梢愿鶕?jù)需要?jiǎng)?chuàng)建指定的遠(yuǎn)程對(duì)象,而關(guān)閉時(shí),則Disconnect之前實(shí)例化的對(duì)象。

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

客戶(hù)端激活模式

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

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

當(dāng)我們要啟動(dòng)/關(guān)閉指定對(duì)象時(shí),只需要用Disconnet()方法來(lái)注銷(xiāo)工廠(chǎng)類(lèi)對(duì)象就可以了。

以上是“Microsoft .Net Remoting基礎(chǔ)的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!

當(dāng)前文章:Microsoft.NetRemoting基礎(chǔ)的示例分析-創(chuàng)新互聯(lián)
地址分享:http://muchs.cn/article28/dpjejp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站內(nèi)鏈、微信小程序、服務(wù)器托管、網(wǎng)站營(yíng)銷(xiāo)、網(wǎng)站排名品牌網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司