一、引言 隨著Internet的發展,軟件系統已經從客戶服務器系統發展到服務器/瀏覽器系統,但是隨著基于Internet應用的深化和基于Web計算的概念的提出,新的計算模型不再是簡單的依賴于瀏覽器作為客戶端。 首先,我們來簡單看一下計算模型的發展歷史,一開始,由于個人計算機還尚未普及,而且計算機價格都比較昂貴,我們一般采用主機模式來進行計算,也就是說客戶實際上是通過終端和大型主機進行連接,主機分配一定的CPU時間和磁盤空間給用戶,所有用戶的計算實際上都是在主機上完成的。這樣對主機的要求非常高,要求主機上的操作系統必須是高度可靠、安全的。在這個時候,流行的是IBM的大型機的操作系統,這里注意的是,即使到現在,很多銀行依然通過這個模式進行運做,因為容易進行集中管理和維護,客戶端實際上僅僅是一個終端的功能,從某一個角度講就是主機屏幕的延伸。 隨著分布式概念的提出和微機功能的增強,軟件業又提出了客戶/服務器的計算模型,把一些非關鍵的任務(比如圖形界面的顯示,數據的顯示格式確定,數據的處理等等)放到客戶端進行執行,這樣相對就減輕了對服務器的負擔。但是這種模式一般是基于局域網范圍內進行的(比如在九十年代非常流行的一些基于數據庫的信息系統就是根據這種模式構建的)。 隨著Internet的發展和企業之間電子交互的需求的出現,出現了基于數據庫/Web服務器/瀏覽器這樣的計算模型,這種模型實際上是基于全球網絡范圍內進行的,客戶端統一的以瀏覽器的形式表現給用戶,用戶通過HTTP協議把任務提交給Web服務器,Web服務器通過和數據庫和應用服務器的交互把結果通過HTTP協議傳遞給客戶端,然后客戶端再通過瀏覽器顯示結果。在這種模式下的關鍵是數據傳遞的安全性和事務性這兩個問題,因為HTTP本質是是一個無狀態的連接,所以事務處理就變得非常重要,同時因為整個業務是基于全球網絡體系結構的,所以安全性也變成一個值得關注的問題。 隨著Internet上計算任務的復雜化和業務的多樣化,自然而然就產生了以Web為中心進行計算的需求。從本質上說,對Internet應用的復雜化使我們當前的操作系統都顯得力不從心,因為我們當前的操作系統一般都是基于單機或者局域網系統的,而如何把操作系統擴展到整個Internet計算這個范疇內,就成了所有操作系統軟件生產廠商所必須考慮的問題。 基于Web的軟件系統的例子有很多,比如Napster,它允許在用戶之間進行磁盤內容的共享,從某一種角度講,它的基本概念是要建立一個基于Web的文件系統,這個文件系統包含了所有參與者本身的文件系統。這樣參與者之間就可以進行文件的共享,通過輸入一定的查詢條件,我們就可以在其他的用戶的存儲設備上找到相應的文件并進行下載(當然這種概念在局域網中早就存在,現在Napster把這個概念擴展到了整個Internet領域而已)。 二、Web服務體系結構分析 這種基于Internet類型應用的出現使我們需要一個嶄新的框架結構來進行程序的設計,我們需要一個快速和方便的方法進行代碼的編寫并且能夠和Internet上其他的程序進行交互。當然在計算機之間進行數據和信息交互這個概念并不是很新,比如通過RPC,DCOM和CORBA等都可以實現不同計算機上的進程之間的交互。但是它們都有一個致命的缺點:它們需要進行交互的機器具有相似的系統,比如MSMQ只能和MSMQ進行對話,DCOM客戶端只能和DCOM服務器端進行交互。 而我們真正需要的是一個通用的開發框架,也就是說不管系統的那一端是什么東西,我們這一端都可以和它進行信息的交互。它的本質意義就是說兩端的操作系統不僅可以是異構的(比如一端可以是Windows,另一端可以是UNIX),而且實現的語言也可以是異構的(一端可以用C++實現,另一端可以用VB實現)。 如果大家還記得在DCOM進行交互時的數據列集(Marshal)這個概念的話,那么就可以理解基于Internet異構系統通訊的關鍵點是什么了。一個就是通訊的標準,兩個進程需要采用標準的協議進行通訊,另外一個就是數據的打包,數據應該采用一致的形式進行打包和解包。當前基于Internet最流行的傳輸協議就是HTTP,所有的Web瀏覽器都通過這個協議和Web服務器進行通訊并得到相關的網頁。而數據的打包也需要采用一定的標準,當前出現的跨平臺的信息編碼的標準就是XML。因為HTTP和XML都是工業的標準,并不和任何平臺,廠商掛鉤,所以基于這兩種標準構建的系統無疑在任何環境是都是有生命力的。 為了創建一個Web服務,我們所需要做的工作就是編寫一個.Net服務對象,使它被異地進程的調用就象能夠被本地的客戶端直接調用一樣。實際上是通過給它標記一定的屬性來實現的,使它能夠被Web客戶端所使用。通過ASP.NET,這個.Net服務對象就能夠接受來自客戶端的請求(通過HTTP協議傳輸的)。也就是說.Net服務對象能夠和任何使用HTTP和XML標準的進程進行通訊,你也不需要考慮Web通訊的體系結構,操作系統已經幫你搞定了這一切。 從服務對象的角度來講,一個客戶和服務對象之間的通訊可以用下面的形式表示: 1. 從客戶端的HTTP請求到達,其中參數可能包含在URL中,也可能包含在一個單獨的XML文件中 2. ASP.NET根據.asmx文件的指定創建對象 3. ASP.NET調用對象的某一個特定的方法 4. 對象把結果返回給ASP.NET 在客戶端,.NET提供了Proxy類用來快速方便的和服務器提供的Web服務進行交互,通過開發工具得到Web服務的描述,然后就可以產生一個包含一些功能函數的Proxy類,注意,在這里我們可以使用任何類型的語言來開發客戶端,當客戶端調用其中的某一個函數的時候,Proxy就會產生一個HTTP請求并把它發送給服務器,當服務器響應返回的時候,Proxy能夠對結果進行解析并返回給調用該函數的客戶端。這樣,就保證了客戶端能夠通過HTTP和XML無縫的和Web服務器進行信息的交互。 從客戶端的角度來講,一個客戶和服務對象之間的通訊可以用下面的形式表示: 1. 在運行時刻,客戶端產生一個Proxy對象 2. 客戶端調用Proxy中的一個方法 3. Proxy把調用轉換成HTTP和XML形式,并通過Internet發送到服務器端 4. Proxy通過HTTP協議得到以XML形式表現的結果,并轉化成相應的結果值返回給客戶 三、Web服務的編寫 這里我們可以寫一個最簡單的Web服務來說明這種新技術的使用,該Web服務以字符串的形式提供當前的服務器的時間(可以精確也可以不精確到秒)。程序是以標準的標記<%...%>"開始的,在該標記符內,WebService告訴ASP.NET該頁的代碼是作為一個Web服務出現的。Language告訴ASP編譯這個頁所使用的語言是VB,然后ASP.NET就會使用Visual Basic.NET來進行代碼的編譯。CLASS屬性告訴ASP.NET當前類對象的名稱為TimeService。 具體的代碼如下: <%@ WebService Language="VB" Class="TimeService"%> ' 引入名字空間' 需要Web Service Imports System Imports System.Web.Services ' 建立一個新的類,該類必須繼承系統提供的基類WebService Public Class TimeService : Inherits WebService '在類中建立我們所需要的函數,并標記為WebMethods Public Function <WebMethod()> GetTime (ShowSeconds as Boolean) As String ' 完成該函數的功能:發現當前的時間,格式化,并以字符串形式返回 Dim dt as DateTime If (ShowSeconds = TRUE) Then GetTime = dt.Now.ToLongTimeString Else GetTime = dt.Now.ToShortTimeString Endif End Function End Class 為了允許開發人員使用Web服務來開發客戶端應用,需要在設計和開發的時候給他們提供一定的信息。比如,一個Web服務的客戶端需要知道Web服務所暴露的方法和相關的參數以及所支持的協議,這個和一個標準的COM所攜帶的類型庫的概念很類似。但是類型庫是COM所專用的,而我們所提供的方法應該是和具體的體系結構無關的,所以需要編寫一個通用的服務方法的描述。ASP.NET提供了一個可描述的服務,當編譯一個WEB服務的時候,ASP.NET提供了一個文件列表用來說明服務所支持的協議,它所提供的方法和參數等等。這個文件是 XML形式編碼并使用稱為SDL(Service Descriptor Language)的語言進行描述的?梢酝ㄟ^http://WebServer/ specifiedDirectory /TimeService.asmx?SDL這樣的形式來得到SDL語言。 當我們用VB或者VC來編寫COM組件的時候,我們一般需要編寫一個類型庫來描述該COM組件所能提供的功能,而對ASP.NET來說,它能夠自動的生成一個SDL文件,也可以先編寫一個SDL文件,然后通過系統工具生成一個服務的模板文件。 一個典型的SDL文件具體內容如下: <!-- 標準的XML頭,描述版本信息和其他相關信息--> <?xml version="1.0"?> <!-- 元素serviceDescription是文件的根節點,里面的內容是對Web服務的描述 --> <!-- 值TimeService表示服務的名稱 --> <serviceDescription name="TimeService"> <!-- 元素serviceDescription包括兩個子元素,一個是協議描述,另外一個是文檔的schema描述 --> <!-協議描述用來告訴客戶端開發人員我這個Web服務所支持的協議,并告訴他請求和響應數據的編碼格式 --> <!-這里我們描述了HTTP GET的操作,一般有三種方法HTTP GET,HTTP POST和SOAP三種方法 --> <httpget> <service> 。紃equestResponse name="GetTime" href="http://WebServer/specifiedDirectory/TimeService.asmx/GetTime"> 。紃equest> 。紁aram name="ShowSeconds"/> 。/request> 。紃esponse> 。糾imeXml ref="s1:string"/> 。/response> 。/requestResponse> </service> </httpget> <!- 這里可以對其他的協議進行描述,比如httpget 和 soap -> <!- schema表示對服務的抽象定義,它和采用什么協議和它進行通訊無關 -> <!- 它實際上包含了對服務包含的函數的名稱,參數和返回值的表示 -> <schema> <element name="GetTime"> 。糲omplexType> 。糴lement name="ShowSeconds" type="boolean"/> 。/complexType> </element> 。糴lement name="GetTimeResult"> 。糲omplexType> 。糴lement name="result" type="string" nullable="true"/> 。/complexType> 。/element> </schema> </serviceDescription> 四、Web服務的客戶端的編寫 現在讓我們來看一下如何編寫客戶端的代碼。我們知道ASP.NET實際上是在偵聽三種類型的數據包,它們包括HTTP GET,HTTP POST和SOAP。 1.采用HTTP GET的方法: 其基本格式如下: http://webserver/specifiedDirectory/timeservice.asmx/GetTime?ShowSeconds=TRUE 2.采用HTTP POST的方法: 實際上就是通過HTML的FORM格式來發送請求,其主要的代碼如下 <form METHOD="POST" ACTION=" http://webserver/specifiedDirectory/timeservice.asmx/GetTime"> <p>是否顯示秒?</p> <blockquote> <p> <input TYPE="RADIO" NAME="ShowSeconds" VALUE="True" CHECKED> True <input TYPE="RADIO" NAME="ShowSeconds" VALUE="False">False<br> </p> </blockquote> <input TYPE="SUBMIT" VALUE="Submit Form"> <input TYPE="RESET" VALUE="Reset Form"> </form> 3.采用SOAP的方法: 服務器接受通過HTTP POST請求發送的數據包,數據包以一個SOAP包的格式保存。注意這里的SOAP包實際上是一個XML文檔,SOAP包中包含了函數的名稱和相關的參數,當SOAP包達到服務器的時候,APS.NET可以識別這個SOAP包,然后提取包中所包含的方法和相應的參數并創建這個對象,然后執行對該方法的調用。并把結果以XML文檔的形式返回到客戶端。這里的關鍵是合成一個有效的SOAP包,并對返回的XML文檔進行有效的信息提取。 這里我們可以用如下的VB代碼來實現基于SOAP的客戶端。 Private Const QuoteTemplate = "<?xml version=""1.0""?> <Envelope xmlns = ""http://schemas.xmlsoap.org/soap/envelope/"" > <Body> <GetTime xmlns:xsi=""http://www.w3.org/1999/XMLSchema-instance"" xmlns=""http://tempuri.org/main.xsd""> <ShowSeconds>true</ShowSeconds> </GetTime> </Body> </Envelope>" Private Sub btnGetTime_Click() ' 建立DOM對象 Dim parser As New DOMDocument ' 裝載XML文檔模板 parser.loadXML (QuoteTemplate) ' 設置參數ShowSeconds的值 If (Check1.Value = 0) Then parser.selectSingleNode("/Envelope/Body/GetTime/ShowSeconds").Text = "false" Else parser.selectSingleNode("/Envelope/Body/GetTime/ShowSeconds").Text = "true" End If ' 把XML文檔的內容放到EDIT控件txtSend中 txtSent.Text = parser.xml ' 使用Microsoft Internet transfer 控件執行HTPP POST操作 Inet1.Execute txtURL.Text, "POST", parser.xml, "Content-Type: text/xml" + vbCr + vbLf +"SOAPAction: http://tempuri.org/GetTime" End Sub ' 控件Inetl1的消息觸發處理 Private Sub Inet1_StateChanged(ByVal State As Integer) If (State = 12) Then ' 讀取從服務器端返回的數據 Dim bar As String bar = Inet1.GetChunk(4096) ' 把服務器端返回的數據存放到EDIT控件txtReceived中 txtReceived.Text = bar ' 把返回的字符串存放到XML文檔中 Dim DocIn As New DOMDocument DocIn.loadXML (bar) ' 利用DOM模型得到元素price的值 Dim Price As IXMLDOMNode Set Price = DocIn.selectSingleNode("SOAP:Envelope/SOAP:Body/GetStockQuoteResponse/price") If (Price Is Nothing) Then txtPrice = "(error)" Else txtPrice = Price.Text End If End If End Sub 4.通過智能的SOAP代理進行操作 這種方法需要使用Visual Studio.NET開發工具,程序通過SDL文件讀取對Web服務的描述并產生一個Proxy(注意這個Proxy可以使用一定的工具自動產生),該Proxy繼承了基類Web.Services.Protocols.SoapClientProtocol。Proxy類包含了一個屬性稱為Path,這個屬性指出服務器的URL地址,它包含一個從SDL文件中得到的缺省的值,客戶端通過方法Invoke來激活Proxy中的方法(比如A),該方法(A)然后創建一個SOAP包,包中包含了方法的名稱和參數,然后通過HTTP協議把它發送給服務器。當SOAP響應包從服務器中返回的時候,基類就對返回的值進行解析并把它返回給Proxy,再由Proxy返回給客戶。然后你可以使用具體的語言來操作這個Proxy,當前支持的語言有Visual Baisc.NET,C#和JavaScript,但不支持C++。 下面是用VB.NET編寫的和Proxy進行交互的代碼: Public Class Form1 Inherits System.WinForms.Form ' 當點擊按紐GetTimeSynch時的處理(同步處理) Protected Sub btnGetTimeSynch_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) ' 產生一個新的Proxy類的對象實例 Dim ThisTimeServiceProxy As New TimeService() ' 設置由用戶指定的屬性Path的值 ThisTimeServiceProxy.Path = TextBox2.Text ' 實際調用方法,并把結果返回到文本框中 ' CheckBox1的值表示方法GetTime的參數值 TextBox1.Text = ThisTimeServiceProxy.GetTime(CheckBox1.Checked) End Sub Dim AsyncTimeServiceProxy As TimeService Dim AsyncResult As IAsyncResult ' 當點擊按紐GetTimeASynch時的處理(異步處理) Protected Sub btn_BeginGetTimeAsync_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) AsyncTimeServiceProxy = New TimeService AsyncResult = AsyncTimeServiceProxy.BeginGetTime(CheckBox1.Checked, Nothing, Nothing) End Sub ' 檢查當前的操作是否結束 Protected Sub btnPollForComplete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) If (AsyncResult.IsCompleted) Then MsgBox("Complete") Else MsgBox("Not Complete") End If End Sub ' 得到采用異步方法調用所得到的結果 Protected Sub btnEndGetTimeAsync_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) TextBox1.text = AsyncTimeServiceProxy.EndGetTime(AsyncResult) AsyncTimeServiceProxy = Nothing End Sub End Class 我們知道,基于Internet的計算環境要比基于桌面的環境要復雜的多,而且交互時間也要長的多,比如對一個最簡單的Web服務的調用可能就要花費5到10秒甚至更長的時間,而在這個時候你不能指望用戶愿意傻乎乎的一直等在那里。換句話說,因為桌面交互時間非常短,我們可以采用同步調用的方法來調用組件的一個方法,但是在基于Internet計算的時候,我們必須采用異步調用的方法。 上面的VB.NET代碼中也演示了異步調用的方法,實際上Proxy本身就包含了一個方法稱為Beginmethodname,就我們具體例子而言就是BeginGetTime,調用這個方法以后,程序會立刻返回,然后可以做其他想做的事情,等你需要看結果的時候,你需要調用方法Endmethodname,然后得到結果。當然如果在結果沒有返回前你調用方法Endmethodname的話,程序就會阻塞直到從服務器中得到結果。為了防止整個界面處于阻塞狀態,實際實現的時候,你可以在一個工作者線程中調用Endmethodname方法。 五、結論 實際上,幾乎所有的編程人員都希望編寫能在Internet上進行交互的程序,而不管他們的運行平臺到底是什么。而通過使用HTTP和XML技術,我們基本上就能夠達到這個功能?蛻舳酥灰褦祿訶ML的形式進行打包,以HTTP形式進行傳送,服務器端就能夠進行處理,而在服務器端,它通過創建一個SDL文件來描述服務對象所能夠提供的方法和參數,這樣客戶端就可以正確的對服務對象進行調用。同時,一個Proxy的生成器提供了一個到達Web服務的函數集,使客戶端程序編寫的工作變的更加簡單。 需要說明的是,微軟即將推出的Visual Studio.NET這套開發工具提供了開發Web服務的集成環境,而這種Web服務的體系結構同時也對Web應用提供了安全級別,這些工具使開發人員能夠快速有效的構建基于Internet的應用系統。 |
延伸閱讀
文章來源于領測軟件測試網 http://www.kjueaiud.com/