上一節我們學習了網絡分布式編程中的MSMQ消息隊列技術.本節我們將學習分布式編程的另外一個重要的技術.Net Remoting,文章的結構還是先討論基本概念,再來探討具體的技術實現,希望能和大家一起交流學習.
.Net Remoting技術,我們可以將其看作是一種分布式處理方式。作為應用程序之間通信的一種機制,.Net Remoting與MSMQ消息隊列不同,它不支持離線脫機消息,另外只適合.Net平台間程序的通信.從微軟的產品角度來看,可以說Remoting就是分布式組件DCOM的一種升級,它改善了很多功能,並極好的融合到.Net平台下。.NET Remoting 提供了一種允許對象通過應用程序域與另一對象進行交互的框架。這也正是我們使用Remoting的原因。為什麼呢?在Windows操作系統中,是將應用程序分離為單獨的進程。這個進程形成了應用程序代碼和數據周圍的一道邊界。如果不采用進程間通信IPC(Internet Process Connection)機制,則在一個進程中執行的代碼就不能訪問另一進程。這是一種操作系統對應用程序的保護機制。然而在某些情況下,我們需要跨過應用程序域,與另外的應用程序域進行通信,即穿越邊界(參考MSDN)。
.Net Remoting的通信架構如下圖.
我們可以通過上圖簡單理解一下.Net Remoting的通信框架,而現在來介紹一下其中涉及到的幾個比較重要的概念:
1.通道(channel),
在.Net Remoting中是通過通道(channel)來實現兩個應用程序域之間對象的通信的。首先,客戶端通過Remoting,訪問通道以獲得服務端對象,再通過代理解析為客戶端對象。這就提供一種可能性,即以服務的方式來發布服務器對象。遠程對象代碼可以運行在服務器上(如服務器激活的對象和客戶端激活的對象),然後客戶端再通過Remoting連接服務器,獲得該服務對象並通過序列化在客戶端運行。
通道(channel)有4種嚴格說,很多資料上說有2-3種.應該有4種.HttpChanel和TcpChanel.
(1)HttpChannel。在將遠程對象駐留在 ASP.NET 中時,可以使用此通道。此通道使用 HTTP 協議在客戶端和服務器之間發送消息。可以使用HTTP協議中的加密機制.需要主機名字和端口號.
(2)TcpChannel。在將遠程對象駐留在 操作系統服務或其他可執行文件中時,此通道使用 TCP 套接字在客戶端和服務器之間發送消息。同樣需要提供主機名字和端口號.不提供任何內置的安全功能。
(3)IPCChanel,進程間通道,只使用同一個系統內,進程之見的通信.不需要需要主機名字和端口號.
(4)自定義通道 。自定義的傳輸通道可以使用任何基本的傳輸協議UDP\SMTP\IPX\消息排隊等機制進行通信.用戶可以根據需要自定義方式協議,因此.Net Remoting相對其他機制更加的靈活。不提供任何內置的安全功能。
2.遠程對象(Obeject)
在Remoting中要傳遞的對象,設計者除了需要了解通道的類型和端口號之外,無需再了解數據包的格式。但必須注意的是,客戶端在獲取服務器端對象時,並不是獲得實際的服務端對象,而是通過代理獲得它的引用。遠程對象要繼承自MarshalByRefObject類,這個可以使遠程對象在remoting應用通信中使用,支持對象的跨域邊界訪問.
3.激活方式
(1)服務器端激活,又叫做知名WellKnown方式,是因為服務器應用程序在激活對象實例之前會在一個眾所周知的統一資源標識符(URI)上來發布這個類型。然後該服務器進程會為此類型配置一個WellKnown對象,並根據指定的端口或地址來發布對象。服務器端激活又分為SingleTon模式和SingleCall模式兩種。SingleTon模式:此為有狀態模式。如果設置為SingleTon激活方式,則Remoting將為所有客戶端建立同一個對象實例。SingleCall模式:SingleCall是一種無狀態模式。一旦設置為SingleCall模式,則當客戶端調用遠程對象的方法時, Remoting會為每一個客戶端建立一個遠程對象實例,至於對象實例的銷毀則是由GC自動管理的。
(2)客戶端激活。與WellKnown模式不同, Remoting在激活每個對象實例的時候,會給每個客戶端激活的類型指派一個URI。客戶端激活模式一旦獲得客戶端的請求,將為每一個客戶端都建立一個實例引用。SingleCall模式和客戶端激活模式是有區別的:首先,對象實例創建的時間不一樣。客戶端激活方式是客戶一旦發出調用的請求,就實例化;而SingleCall則是要等到調用對象方法時再創建。其次,SingleCall模式激活的對象是無狀態的,對象生命期的管理是由GC管理的,而客戶端激活的對象則有狀態,其生命周期可自定義。其三,兩種激活模式在服務器端和客戶端實現的方法不一樣。尤其是在客戶端,SingleCall模式是由 GetObject()來激活,它調用對象默認的構造函數。而客戶端激活模式,則通過CreateInstance()來激活,它可以傳遞參數,所以可以調用自定義的構造函數來創建實例。(詳細參考MSDN)
(4)代理Proxy,客戶端訪問的不能直接訪問遠程對象,它是通過代理來訪問代理上的方法.代理對象又分為透明代理和真實代理,區別是,在透明代理上,客戶通過Invoke調用的是遠程對象上真實代理的方法.然後把消息再傳遞給通道.
好了,介紹到此我們也基本了解.Net Remoting相關的知識,下面我們來學習的是具體的編程實現部分.程序大體分為3個部分遠程對象\服務器\可戶端.現在我們來分別實現.服務器端要添加引用System.Runtime.Remoting的程序集.
1.遠程對象(RemoteOject),也就是我們遠程要訪問的對象.首先定義一個Class,繼承MarshalByRefObject,可以使用在remoting應用中,支持對象的跨域邊界訪問.具體代碼如下:
1namespace RemoteObject
2{
3 //創建遠程對象.繼承MarshalByRefObject,可以使用在remoting應用中,支持對象的跨域邊界訪問
4 public class MyRemoteObject : MarshalByRefObject//訪問遠程對象需要通過代理
5 {
6 //簡單的例子,實現加法功能的方法AddForTcpTest,這個遠程對象可以實現封裝業務邏輯或者數據訪問等操作。
7 //
8 public int AddForTcpTest(int a, int b)
9 {
10 return a + b;
11 }
12 //實現減法功能的方法MinusForHttpTest,測試HTTP通道。
13 public int MinusForHttpTest(int a, int b)
14 {
15 return a - b;
16 }
17 //實現乘法功能的方法MultipleForIpcTest,測試Ipc通道
18 public int MultipleForIpcTest(int a, int b)
19 {
20 return a * b;
21 }
22 }
23
24}
25
對象分別定義了3種方法,目的是為了測試3種不同的通道方式的效果. //簡單的例子,實現加法功能的方法AddForTcpTest,這個遠程對象可以實現封裝業務邏輯或者數據訪問等操作。實現減法功能的方法MinusForHttpTest,測試HTTP通道。實現乘法功能的方法MultipleForIpcTest,測試Ipc通道.
2服務器端,注冊通道,以便進程間通信,我們這裡注冊了三種通道,分別是HttpChanel\TcpChanel\IPCChanel.具體代碼如下:
1namespace NetRemotingHost
2{
3 class NetRemotingHost
4 {
5 //創建宿主應用程序
6 static void Main(string[] args)
7 {
8
9 /**///////////////////////////////////////創建三種通道/////////////////////////////////////////////////
10 ///創建Tcp通道,使用端口10001
11 TcpChannel chanTcp = new TcpChannel(10001);
12 /**////創建Http通道,使用端口10002
13 HttpChannel chanHttp = new HttpChannel(10002);
14 /**////創建IPC通道,使用端口10003,IPC只適合同系統內進程的通信,所以不需要設置端口和主機名
15 IpcChannel chanIPC = new IpcChannel("FrankTestIpc");
16 /**///////////////////////////////////////注冊通道//////////////////////////////////////////////////
17
18 ///注冊TCP通道
19 ChannelServices.RegisterChannel(chanTcp);
20 /**////注冊HTTP通道
21 ChannelServices.RegisterChannel(chanHttp);
22 /**////注冊IPC通道
23 ChannelServices.RegisterChannel(chanIPC);
24
25 /**/////////////////////////////////////////打印通道/////////////////////////////////////////////////
26 // 打印TCP通道的名稱.
27 Console.WriteLine("The name of the TCPChannel is {0}.",
28 chanTcp.ChannelName);
29 // 打印TCP通道的優先級.
30 Console.WriteLine("The priority of the TCPChannel is {0}.",
31 chanTcp.ChannelPriority);
32
33 // 打印Http通道的名稱.
34 Console.WriteLine("The name of the HttpChannel is {0}.",
35 chanTcp.ChannelName);
36 // 打印Http通道的優先級.
37 Console.WriteLine("The priority of the HttpChannel is {0}.",
38 chanTcp.ChannelPriority);
39
40 // 打印IPC通道的名稱.
41 Console.WriteLine("The name of the IpcChannel is {0}.",
42 chanTcp.ChannelName);
43 // 打印IPC通道的優先級.
44 Console.WriteLine("The priority of the IpcChannel is {0}.",
45 chanTcp.ChannelPriority);
46 /**////////////////////////////////////////////注冊對象/////////////////////////////////////////////////
47 //注冊對象MyRemoteObject到Net Remoting運行庫
48 //配置遠程通信基礎框架.第2個參數是對象的URI,“RemoteObject.MyRemoteObject”,客戶端URL一定要和這個匹配,不然會出現查找不到代理對象的異常
49 RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemoteObject.MyRemoteObject), "RemoteObject.MyRemoteObject", WellKnownObjectMode.Singleton);
50 //遠程對象激活方式是單件激活模式,每次調用共享一個對象,SingleCall激活模式會在每次調用的時候產生一個新對象
51
52 //向信道暴露一個IPC遠程對象.
53 //RemotingConfiguration.RegisterWellKnownService(Type(typeof(RemoteObject), "RemoteObject.rem", System.Runtime.Remoting.WellKnownObjectMode.Singleton);
54
55 /**////////////////////For Debug/////////////////////////////////////////////////////////////////////////
56 Console.WriteLine("Press any key to exit!");
57 System.Console.ReadLine();
58
59 }
60
61
62 }
63}
64
創建Tcp通道,使用端口10001, 創建Http通道,使用端口10002,創建IPC通道,使用端口10003,IPC只適合同系統內進程的通信,所以不需要設置端口和主機名.然後調用ChannelServices類的靜態方法RegisterChannel進行注冊.最後一步注冊對象MyRemoteObject到Net Remoting運行庫,同時要先配置遠程通信基礎框架.第2個參數是對象的URI,“RemoteObject.MyRemoteObject”,客戶端URL一定要和這個匹配,不然會出現查找不到代理對象的異常.WellKnownObjectMode.Singleton);遠程對象激活方式是單件激活模式,每次調用共享一個對象,SingleCall激活模式會在每次調用的時候產生一個新對象.
3客戶端是控制台程序(實際項目類型可以替換,這裡只是為了作為例子選擇控制台類型).配置文件進行的設置如下:
1<configuration>
2 <appSettings>
3 <add key="ServiceURLTcp" value="tcp://localhost:10001/RemoteObject.MyRemoteObject"/>
4 <add key="ServiceURLHttp" value="http://localhost:10002/RemoteObject.MyRemoteObject"/>
5 <add key="ServiceURLIpc" value="ipc://FrankTestIpc/RemoteObject.MyRemoteObject"/>
6 </appSettings>
7 <system.runtime.remoting>
8
9 </system.runtime.remoting>
10</configuration>
配置文件設置的是具體通道的URL信息.具體c#實現代碼如下:
1
2namespace RemoteClient
3{
4 class MyClient//客戶端
5 {
6 [STAThread]//主線程,建立客戶端程序:注冊通道,根據URL得到對象代理,使用代理調用遠程對象
7
8 static void Main(string[] args)
9 {
10 //為遠程對象創建代理
11 //代理的優勢在於不僅可以跨域訪問對象還可以跨進程,和系統,使用TCP通道
12 try
13 {
14 /**/////
15 RemoteObject.MyRemoteObject proxyObjectTcp = (RemoteObject.MyRemoteObject)Activator.GetObject(typeof(RemoteObject.MyRemoteObject), System.Configuration.ConfigurationSettings.AppSettings["ServiceURLTcp"]);
16 //通過代理訪問對象的方法,輸出結果
17 Console.WriteLine("This call object by TcpChannel,100+200 = {0}", proxyObjectTcp.AddForTcpTest(100, 200));
18
19 /**///////
20 RemoteObject.MyRemoteObject proxyObjectHttp = (RemoteObject.MyRemoteObject)Activator.GetObject(typeof(RemoteObject.MyRemoteObject), System.Configuration.ConfigurationSettings.AppSettings["ServiceURLHttp"]);
21 //通過代理訪問對象的方法,輸出結果
22 Console.WriteLine("This call object by HttpChannel,100-200 = {0}", proxyObjectHttp.MinusForHttpTest(100, 200));
23
24 /**///// 注冊一個遠程對象的客戶端代理.
25 //System.Runtime.Remoting.WellKnownClientTypeEntry remoteType = new System.Runtime.Remoting.WellKnownClientTypeEntry(typeof(RemoteObject.MyRemoteObject), "ipc://FrankTestIpc/RemoteObject.MyRemoteObject");
26 //System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownClientType(remoteType);//如果需要客戶端和住進程通訊,要在客戶端注冊代理,方式和服務器端注冊注冊遠程對象的代理相同
27
28 RemoteObject.MyRemoteObject proxyObjectIpc = (RemoteObject.MyRemoteObject)Activator.GetObject(typeof(RemoteObject.MyRemoteObject), System.Configuration.ConfigurationSettings.AppSettings["ServiceURLIpc"]);
29 //通過代理訪問對象的方法,輸出結果
30 Console.WriteLine("This call object by IpcChannel,100*200 = {0}", proxyObjectIpc.MultipleForIpcTest(100, 200));
31 }
32 catch (Exception e)
33 {
34 throw e;
35 }
36 finally
37 {
38
39 }
40 //For Debug
41 Console.WriteLine("Press any key to exit!");
42 Console.ReadLine();
43 }
44
45 }
46}
47
主進程通過配置獲取遠程對象的信息,為遠程對象創建代理,代理的優勢在於不僅可以跨域訪問對象還可以跨進程,和系統,使用TCP通道,降低系統耦合性.最後客戶端通過代理訪問遠程對象的方法,輸出結果.首先要運行服務器端,其次是客戶端,IDE使用的是Visual Studio 2005/2008.其次注意項目引用.運行後的結果顯示如下.
服務器:
顯示3個通道都注冊成功.
客戶端:
客戶端分別通過3種方式調用遠程對象,進行運算.測試成功!
小結:以上就是全部的.Net Remoting的實現過程.當然.Net Remoting知識范圍很廣,還有異步調用,安全等.以後再做深入的學習.接下來一節我打算寫關於Enterprise Services的文章,其中會涉及到COM+的知識,如COM+中事務機制.我們有必要好好學習一下.希望本文的能給大家在WCF學習上對.Net Remoting技術的理解有所幫助.大家有問題可以一起交流~
本文配套源碼