深入研究VB.Net遠程調用對象的機制
第一節 引言
在.NET出現以前,COM和DCOM可以讓你很容易地用運行在另一進程或者網絡上另一臺機器上的對象來進行交互作用。然而現在,.NET結構提供一種與之不同的機制--遠程調用,用于在你的客戶應用程序和遠程對象之間的內部處理和LAN通訊。在本文中,我將介紹如何通過在Visual Basic.NET(VB.NET)中創建一個類庫來使用遠程對象,調用來自于另一進程或者網絡上另一臺機器的方法的客戶端應用程序。如圖

你仍然可以在VB.NET中使用COM和DCOM,VB.NET支持一個交互機理,你可以用來在COM組件中調用來自.NET的代碼。然而,這個機制是為了能夠繼承以前的系統支持而設計的而不是為了新技術開發的,所以你應該著眼于更多現在新的技術。
另外一個替代的機制就是使用Web服務調用遠程對象,當你使用 Web服務時,你不能進行一個對象的相同的實例的一個以上的方法調用,一個新對象實例服務每次只能調用一個。使用Web服務還要求你在對象運行的機器上安裝Internet Information Server ( IIS ),但是你需要的可能是一種不需要安裝IIS的輕量級解決方案。
遠程調用是最類似于DCOM的.NET中的技術。在Beta 1版中,.NET遠程調用比 DCOM需要更多的準備工作,但是它是一種簡單的后臺技術,并且承諾當.NET正式版本發布的時候會更加容易。你可以在相同的機器上的一個單獨的進程中使用遠程調用激活對象,或通過網絡激活運行在另外一臺機器上的對象。使用遠程調用,你可以進行對象的相同的實例上的更多的方法調用,或者你可以配置遠程調用象 Web服務那樣動作,并且為每一個方法調用創建新的對象實例。
為了使用遠程調用工作,你需要安裝.NET Framework SDK,并最好安裝集成Visual Studio.NET(VS.NET)。讓我們在單獨的一臺機器上開始測試,在一個來自客戶端應用程序中的單獨的進程中運行物體的遠程調用過程,然后把這個對象的遠程調用過程移到網絡上的另外一臺機器上。
第二節 了解簡單的遠程調用
雖然你可以使用遠程調用來填補曾由DCOM來滿足的市場,但是遠程調用與DCOM不同,遠程調用使用一個你選擇的網絡接口,而DCOM不同,它使用許多難以預測的網絡接口來與你的對象通訊,這使得遠程調用在大多數的環境中可以很容易的配置。
當你調用一個使用遠程調用的對象時,你通過一個通道進行調用過程:這個遠程調用通訊機制與在網絡上的對象通訊。你可以選擇兩個不同的通道甚至還可以創建你自己的通道。.NET中提供的兩個通道是HTTP通道和一個直接的 TCP socket通道,你使用這些通道中的一個(即使你可能在同一臺機器上)激活一個對象。
如果你使用 HTTP通道,你就得使用HTTP協議和簡單對象訪問協議(SOAP)與遠程調用對象通訊,即使你使用 HTTP,你也未必非得使用端口80,雖然這是Web服務器默認的端口,你可以指定任意端口號作為通信端口。
直接的 TCP socket通道也是類似的,如果你選擇這個通道,所有的通信就使用一個專有協議和一個SOAP的二進制實現來完成。雖然說這有些不好的地方,但是它多多少少能夠快一些,因為把數據編碼成二進制格式的過程比使用SOAP效率更加高,因為它是基于XML的。
每次你的代碼調用遠程調用對象的一個方法,遠程調用子系統把這個方法調用轉換成一條并把它通過選定的通道發送到遠程調用對象中去。遠程調用子系統然后再把這個方法的返回結果轉換成一條消息返回到客戶端應用程序中。在 beta 1中,使用一個單獨的" black box "遠程調用組件處理這個過程,但是在以后將發布的.NET中,這個體系結構將更加開放,將提供更多的可延伸性和可控制性。
和 DCOM一樣,遠程調用對象必須在一個過程中運行。因為 Microsoft Transaction Server ( MTS )和 COM+都不是特意支持 .NET,所以你需要在服務器上實現你自己的主機應用程序來。 你的主機應用程序使用遠程調用服務來在消息通道上監聽。 其實這比聽起來要容易,因為遠程調用為你考慮到所有的細節。 然而,遠程調用調試起來可能會比較困難。 稍后我將討論一些可以使調試變得更容易的技術。
第三節 創建并使用一個類
在你能夠通過網絡調用一個對象之前,你必須有一個類庫。所有的COM對象在你首次創建它們的過程中或者在機器中繼續運行,在 VB.NET中,你可以使用<Serializable()>屬性標記一個類,這樣你的對象就是以字的方式發送,意思是說它是物理上被復制進入正在調用的應用程序過程中的。
顯然,如果你想要激活一個在另外一個過程或另外一臺機器中的對象的方法,你想要那個對象支持遠程調用。要想這樣的話,你可以從.NET系統類庫中的MarshalByRefClass類或MarshalByRefComponent類中取得你的對象,與使用值傳遞的標準System.Object基本類不同,你是通過引用傳遞這些基本類。你也應該避免使用<Serializable>屬性標記這個對象,因為你有可能并不希望這個對象連續,而是在服務器中運行它。
你可以創建一個簡單的類庫來檢測外部遠程調用,在 VS.NET集成環境中,創建一個新類庫項目,取名為NETserver并把默認的 Class1.vb文件重命名為NETclass.vb。默認的情況下,這個類是從Component基本類中派生出來的,它是通過值傳遞的。你可以把 Inherits行改為通過引用傳遞你的類,把你的類改為MarshalByRefComponent的子類:
| Public Class NETclass Inherits System.ComponentModel.MarshalByRefComponent |
你可以添加方法到包含遠程運行的代碼的類上。例如,你可以編寫一個方法從Pubs數據庫中以一個作者ID來檢索作者姓名,使用Imports語句添加一個引用到 System.Data.dll中。
| Imports System.Data Imports System.Data.SQL |
然后,添加一個方法到你的類上,用來檢索相應的數據行并把作者姓名作為結果返回。參見下面列出的程序代碼:
| Public Function GetName(ByVal au_id As String) As String Const dbConn As String = "server=ineroth;uid=sa;pwd=;database=pubs" Dim cm As SQLCommand Dim dr As SQLDataReader Dim SQL As String SQL = "SELECT au_lname, au_fname " & "FROM authors WHERE au_id='" & au_id & "'" cm = New SQLCommand(SQL, dbConn) cm.ActiveConnection.Open() cm.Execute(dr) dr.Read() Return CStr(dr.Item("au_lname")) & ", " & CStr(dr.Item("au_fname")) End Function |
你現在就有了一個可被遠程調用的類了。
你的遠程調用對象需要在它運行的地方有一個過程,并且這個主機過程需要使用遠程系統來監聽傳到你的對象上的請求。 幸運的是,代碼使得這過程很簡單的。添加一個新的Console Application項目到 VS.NET中,在Solution Explorer中,添加新的項目。 你可能需要向下滾動卷軸找到Console Application圖標, 改名為 NEThost。添加一個引用到System.Runtime.Remoting.dll并添加一個 Imports語句,然后使用遠程系統 :
| Imports System.Runtime.Remoting |
在控制臺應用程序中,當應用程序啟動時, Sub Main方法自動運行。 你可以在這個方法中添加幾行代碼來設置遠程系統:
| Sub Main() RemotingServices.ConfigureRemoting("...cfg") Console.WriteLine("Press Console.ReadLine() End Sub |
ConfigureRemoting方法接收文件名并把它當成一個參數。這個文本文件包含配置遠程調用所必需的信息,你需要創建這個文件,我過一會將介紹如何創建。
第四節 保持這個應用程序運行
你還需要確定這個主應用程序直到你準備好時才退出,這個主應用程序一終止,你的遠程對象對任何客戶就變得不可用。在本例中,你保持這個應用程序運行直到用戶按下回車鍵,但在一個實際的應用程序中,你應該選擇為主應用程序生成一個 Windows 2000或 Windows NT服務應用程序,以便它始終能夠運行。
首先,你需要通過點擊 Project菜單引用NETserver項目,然后添加引用和項目標簽(參見圖 2),選擇 NETServer并單擊 OK,在模塊 1的頂部添加一條導入語句:
導入 NETserver

這樣你的類對這個主應用程序就可用了,對于那些能夠創建這個對象的實例的遠程系統就很關鍵。
主配置文件描述了你的對象以及如何到達。使用這個文件來設置主應用程序,以便它能夠在適當的通道里監聽適當的信息。在Solution Explorer中,右擊你的主應用程序并從菜單中選擇Add New Item(添加新的項目)。命名這個文件為 host.cfg。這個文件是一個簡單的文本文件,使用井號(#)作為分隔符,并且它有三行代碼能為你的遠程對象服務。
第一行指出主應用程序的名稱。名稱可以是任何東西,但是客戶機可以使用它來找到主機:
| Name#NEThost |
接下來的這行定義調用的對象,它描述了這個類的類型名稱,這個類所處的集合,應該監聽消息的主機的統一資源標識號(URI)以及對象運行的方式:
| WellKnownObject#NETserver.NETclass#NETserver#NETserver/NETclass.soap#Singleton |
你需要在一個行中輸入前面所有的代碼,在項目之間不留空格。這是 beta 1中隱含的唯一的文件格式;將來,你將能使用 XML來格式化這個文件。
遠程調用支持兩種方式: Singleton和 SingleCall。
第三行配置當與對象通信時使用的通道。本例中使用的HTTP通道, 8085端口:
| Channel#System.Runtime.Remoting#System.Runtime._ Remoting.Channels.HTTP.HTTPChannel#ports=8085 |
在一行中輸入所有的代碼,項目中間不留空格。
一旦你構建了解決方案,你就可以打開一個控制臺窗口并運行主應用程序。以命令行形式進入 NEThostu30446目錄并執行 NEThost.exe。應用程序指出這個網絡類已經被初始化了。它現在監聽來自客戶機的請求。
你還可以以編程方式配置主機,而不使用配置文件。配置文件很好用,因為你可以使用它來改變主應用程序使用的通道或端口而不必重編譯和重新部署。然而,如果你喜歡的話,你也可以跳過配置文件而直接地從代碼中配置遠程服務。它的功能上與使用配置文件相當;你可以選擇任一種方法。
修改Sub Main方法來配置主機:
| Sub Main() Dim c As New Channels.HTTP.HTTPChannel(8085) ChannelServices.RegisterChannel(c) RemotingServices.RegisterWellKnownType("NETserver", "NETserver.NETclass","NETclass", WellKnownObjectMode.Singleton) Console.WriteLine("Press Console.Read() End Sub |
首先,你要創建一個 HTTPChannel對象,初始化它來使用 8085端口,然后使用 ChannelServices注冊這個通道。接下來,你可以注冊遠程對象,只要提供集合名稱、完整的類名、客戶機使用的 URI以及對象的存取方式。
當你使用 DCOM調用一個遠程對象時,你的客戶機代碼與你的項目中直接調用的對象的代碼不同。VB.NET并不保留這個特性,你的 VB.NET客戶應用程序需要包含配置遠程系統的代碼以使遠程對象可用的。不管對象是本地的還是遠程的,調用這個對象上方法的實際代碼都是相同的,但是你需要添加幾行額外代碼來配置這個遠程系統。
在 VS.NET中創建一個新Windows應用程序項目并把它命名為 NETclient,添加一個NETserver集合的引用,并添加一個 Imports語句:
| Imports NETserver |
這個語句使你的客戶應用程序可使用遠程類,你現在可以添加一個按鈕到窗體中,并寫按鈕的代碼來調用對象:
| Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Dim obj As New NETclass() MsgBox(obj.GetName("238-95-7766")) End Sub |
最后,你需要配置遠程服務。使用 NEThost應用程序中相同的代碼來完成這個任務,添加一個 System.Runtime.Remoting.dll的引用并添加一句 Imports語句:
| Imports System.Runtime.Remoting |
在窗體的New方法中添加一行代碼來配置遠程服務:
| RemotingServices.ConfigureRemoting("...cfg") |
與主應用程序一樣,你需要創建一個配置文件以便客戶應用程序知道如何定位和聯絡主應用程序和你的遠程對象。
這個配置文件也是一個簡單的文本文件,就象主機配置文件。它也包含三行代碼,使用井號(#)作為分隔符。第一行代碼標識客戶程序名稱:
| Name#NETclient |
下面的這行代碼標識你的遠程對象集合:
| Assembly#NETserver#NEThost#NETserver.NETclass=HTTP://localhost:8085/NETclass |
在一行中輸入所有的代碼,項目中間不留空格。
你需要指定遠程主應用程序的應用程序名和包含這個遠程對象的集合的名稱。然后提供這個遠程類的完整類名和用戶將訪問的類的完整的URI。從你在主機配置文件中指定的 URI中得到這個 URI,并且指定通信協議(HTTP://)、主機(localhost)和端口(:8085)。
最后,你需要指出與主應用程序通信時使用的通道,這是你包含在主機配置文件中的相同的一行代碼:
| Channel#System.Runtime.Remoting#System.Runtime.Remoting.Channels.HTTP.HTTPChannel |
在一行中輸入所有的代碼,項目中間不留空格。你必須在客戶機和主機之間使用公共的通道;否則,它們就不能夠通信。
第五節 配置客戶端
就象主應用程序一樣,你可以選擇以編程方式配置客戶應用程序而不是使用一個配置文件。然而,在beta 1中,在客戶端上的改動并不像在服務器端上的改動那么容易,因為你還需要改變創建每一個對象實例的方法的代碼。為了不使用New關鍵字來創建對象,你還需要使用Activator.GetObject方法。這利用了一個附帶在客戶機上的配置文件。
改變配置客戶機的方法,只要把調用替換為ConfigureRemoting,如下所示:
| Dim c As New Channels.HTTP.HTTPChannel() ChannelServices.RegisterChannel(c) |
這兩行代碼配置 HTTP通道,以便為使用它做好準備。然后你必須搜尋客戶應用程序,找到使用 New關鍵字創建遠程對象的代碼:
| obj = New NETclass() |
使用這個語句替換前面的那一行語句:
| obj = CType(Activator.GetObject (GetType(NETserver.NETclass), _ "http://localhost:8085/NETclass"), NETclass) |
這行語句很復雜,它調用GetObject方法從指定URI取得一個 NETserver.NETclass類型的對象。然后使用CType函數把得到的結果對象強制轉換成 NETclass類型以便你可以在你的代碼中使用它。
你的客戶機代碼剩余部分就可以保持不變了,如果你決定使用編程配置你的客戶應用程序,我推薦在一個函數中封裝對象的創建過程,如下:
| Private Function NewNETclass() As NETclass Return CType(Activator.GetObject( GetType(NETserver.NETclass),_ "http://ineroth:8085/NETclass") , NETclass) End Function |
然后,當你想得到這個對象的一個引用的話,你可以使用這個函數:
| obj = NewNETclass() |
雖然這還不象使用 New關鍵字那么簡單,它還是封裝代碼來創建遠程對象。
遠程程序很難調試,這是因為你的客戶應用程序有到你的遠程程序直接的引用,當遠程程序失敗時而你的客戶機看上去仍然在工作。這是因為如果遠程調用過程失敗的話,客戶機就會試圖調用對象的局部拷貝。
你可以證明在這個函數返回它的值之前,通過在NETclass中添加一行代碼調用遠程對象來打印一則信息到控制臺窗口:
| Console.WriteLine("Returning " & CStr(dr.Item("au_lname"))_ & ", " & CStr(dr.Item("au_fname"))) |
現在,在你的主應用程序運行的服務器控制臺窗口中,當它被調用時這個功能顯示一則信息。當你運行客戶應用程序時轉換到控制臺窗口并保證它顯示這則信息。事實上,這證明你正在與遠程對象交互。
如果你在連接遠程對象時遇到了麻煩,你可以通過使用瀏覽程序來測試這個主應用程序。在一個控制臺窗口中運行這個主應用程序,然后打開 Internet Explorer并且定位到這個遠程對象的 URI。在我的例子中,你將定位到這個 URI:
| http://localhost:8085/NETclass |
如果主機正確操作,你應該看作為顯示為一個頁面或 XML的遠程對象的SOAP定義(參見圖3)。如果你沒有看,主機可能就沒能正確配置。

你可以使用遠程調用來創建 N層應用程序,只要通過調用 Active Server Pages ( ASP.NET )或者來自 VB.NET客戶端應用程序中的 remote組件。 你也可以通過把 NEThost放進 Windows 2000 Service Application中,來增強這個例子的功能。因為那樣的話,服務器在這臺機器運行的任何時候都可用。你還可以使用它從一個數據庫中返回數據,或者執行其他需要的服務器端處理過程。 你還可以使用 ASP.NET頁面或者 VB.NET Windows應用程序來替換本文中的簡單的例子,這樣你就可以向你的用戶顯示你豐富的才華了。