基于WCF的即時(shí)通訊軟件的設(shè)計(jì)與實(shí)現(xiàn)_第1頁(yè)
基于WCF的即時(shí)通訊軟件的設(shè)計(jì)與實(shí)現(xiàn)_第2頁(yè)
基于WCF的即時(shí)通訊軟件的設(shè)計(jì)與實(shí)現(xiàn)_第3頁(yè)
基于WCF的即時(shí)通訊軟件的設(shè)計(jì)與實(shí)現(xiàn)_第4頁(yè)
基于WCF的即時(shí)通訊軟件的設(shè)計(jì)與實(shí)現(xiàn)_第5頁(yè)
已閱讀5頁(yè),還剩29頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

1、基于WCF的即時(shí)通訊軟件的設(shè)計(jì)與實(shí)現(xiàn) 摘要: 介紹了Microsoft用于構(gòu)建分布式面向服務(wù)架構(gòu)系統(tǒng)的新一代框架WCF的體系結(jié)構(gòu)與技術(shù)要素,并通過(guò)開(kāi)發(fā)一套即時(shí)通信軟件展現(xiàn)了基于WCF構(gòu)架開(kāi)發(fā)分布式應(yīng)用程序的編程步驟與技巧。關(guān)鍵詞:WCF;SOA;分布式;即時(shí)通信 一、前言 自從在微軟提出.NET戰(zhàn)略以來(lái),就針對(duì)建立企業(yè)級(jí)的分布式應(yīng)用先后推出了一系列產(chǎn)品和技術(shù),包括:ASP.NET Web服務(wù)、.NET Remoting、Message Queuing以及Enterprise Service等。這些技術(shù)為基于微軟技術(shù)的軟件研發(fā)人員開(kāi)發(fā)分布式應(yīng)用提供了很大的便利,同時(shí)也各自存在著一些不足。WCF(

2、Windows Communication Foundation)作為微軟基于SOA所推出的.NET平臺(tái)下的新一代框架產(chǎn)品集成了現(xiàn)有技術(shù)的優(yōu)點(diǎn),代表了未來(lái)軟件架構(gòu)設(shè)計(jì)與開(kāi)發(fā)的發(fā)展方向。因此,掌握并能在未來(lái)應(yīng)用中合理運(yùn)用WCF技術(shù),對(duì)于程序員特別是基于微軟技術(shù)開(kāi)發(fā)的程序員而言是十分必要的?;诖耍恼峦ㄟ^(guò)介紹一套即時(shí)通信軟件的具體開(kāi)發(fā)過(guò)程來(lái)展現(xiàn)基于WCF技術(shù)的分布式軟件研發(fā)的基本步驟與高級(jí)技巧。二、WCF概述 對(duì)于一個(gè)好的分布式系統(tǒng)來(lái)講,設(shè)計(jì)時(shí)應(yīng)當(dāng)考慮到異構(gòu)性、開(kāi)放性、安全性、可擴(kuò)展性、故障處理、并發(fā)性以及透明性等問(wèn)題。基于SOAP的Web Service可以實(shí)現(xiàn)異構(gòu)環(huán)境的互操作性,保證了跨平臺(tái)

3、的通信。利用WSE(Web Service Enhancements)可以為ASMX提供安全性的保證。.NET Remoting具有豐富的擴(kuò)展功能,可以創(chuàng)建定制的信道、格式化器和代理程序。Enterprise Service(COM+)提供了對(duì)事務(wù)的支持,其中還包括分布式事務(wù),可實(shí)現(xiàn)故障的恢復(fù)。MSMQ可以支持異步調(diào)用、脫機(jī)連接、斷點(diǎn)連接等功能,利用消息隊(duì)列支持應(yīng)用程序之間的消息傳遞。從功能角度來(lái)看,WCF整合了ASMX、.Net Remoting、Enterprise Service、WSE以及MSMQ等現(xiàn)有技術(shù)的優(yōu)點(diǎn),它提供了一種構(gòu)建安全可靠的分布式面向服務(wù)系統(tǒng)的統(tǒng)一的框架模型,使軟件研發(fā)

4、人員在開(kāi)發(fā)分布式應(yīng)用時(shí)變得更加輕松。1. 面向服務(wù) 既然WCF是一套面向服務(wù)的框架,服務(wù)自然便是WCF中最為重要的概念。服務(wù)是指暴露在外的一系列功能的集合,面向服務(wù)則是指一套構(gòu)建“面向服務(wù)程序”的抽象原則以及最優(yōu)方法。對(duì)于業(yè)務(wù)邏輯的理解,傳統(tǒng)的編程方式認(rèn)為應(yīng)將業(yè)務(wù)邏輯封裝為對(duì)象,該對(duì)象提供了與業(yè)務(wù)相關(guān)的一些功能;而基于WCF的程序設(shè)計(jì)卻更多的是考慮如何提供服務(wù)以及消費(fèi)服務(wù)。與面向組件服務(wù)程序類似,基于SOA 的應(yīng)用程序?qū)⒎?wù)封裝到了單個(gè)邏輯程序當(dāng)中,如圖1所示。圖1封裝服務(wù)的SOA應(yīng)用程序邏輯圖2. WCF體系結(jié)構(gòu) WCF擁有一個(gè)非常靈活的分層體系結(jié)構(gòu),分布式應(yīng)用程序可以使用高級(jí)API或者低級(jí)

5、API編寫。高級(jí)API或者服務(wù)層可以用于調(diào)用方法和事件。服務(wù)層把這些高級(jí)的抽象代碼轉(zhuǎn)換為消息,以使用低級(jí)API上的信道和端口。圖2中顯示了WCF應(yīng)用程序的各個(gè)層。圖2 WCF體系結(jié)構(gòu)圖 WCF提供了對(duì)可靠性、事務(wù)性、并發(fā)管理、安全性以及實(shí)例激活等技術(shù)的有力支持,而這些支持均依賴于如圖3所示的WCF構(gòu)架。 在客戶端,分布式應(yīng)用通過(guò)一個(gè)代理來(lái)轉(zhuǎn)發(fā)對(duì)宿主端所提供服務(wù)的調(diào)用,而代理?yè)碛泻头?wù)相同的操作接口,另外還有一些附加的代理管理方法。這也就意味著客戶端從來(lái)不會(huì)直接調(diào)用服務(wù),即便這個(gè)服務(wù)就在本機(jī)的內(nèi)存中。當(dāng)客戶端代理接收到來(lái)自客戶端的調(diào)用請(qǐng)求后,它將消息通過(guò)信道鏈向下傳遞。每個(gè)信道都會(huì)執(zhí)行相應(yīng)的消息

6、的調(diào)用前處理,例如對(duì)消息的編碼、提供可靠的會(huì)話、對(duì)消息進(jìn)行加密等。客戶端的最后一個(gè)信道則是傳輸信道,根據(jù)配置的傳輸方式發(fā)送消息給宿主。 在宿主端,消息同樣通過(guò)信道鏈進(jìn)行傳輸。與客戶端信道相對(duì)應(yīng),宿主端信道也會(huì)對(duì)消息執(zhí)行相應(yīng)的宿主端的調(diào)用前處理,例如對(duì)消息的解碼、提供會(huì)話管理、對(duì)消息進(jìn)行解密等。宿主端的最后一個(gè)信道則負(fù)責(zé)將消息發(fā)送給消息分發(fā)器(Dispatcher),由分發(fā)器負(fù)責(zé)調(diào)用服務(wù)的實(shí)例。圖3 WCF構(gòu)架示意圖3. WCF的基本技術(shù)要素 作為基于SOA的一個(gè)框架產(chǎn)品,WCF實(shí)際上是構(gòu)建了一個(gè)在互聯(lián)系統(tǒng)中實(shí)現(xiàn)各個(gè)應(yīng)用程序之間通信的分布式框架。它使得系統(tǒng)構(gòu)架師與開(kāi)發(fā)人員在構(gòu)建分布式系統(tǒng)時(shí),能將

7、更多的精力投入到與系統(tǒng)的業(yè)務(wù)邏輯本身的設(shè)計(jì)上來(lái),而無(wú)需過(guò)多的考慮底層通信的實(shí)現(xiàn)及相關(guān)問(wèn)題。WCF最核心的部分是能夠快捷的創(chuàng)建一個(gè)服務(wù),一個(gè)WCF服務(wù)端框架由宿主、端點(diǎn)以及服務(wù)類三部分所組成,如圖4所示。圖4 WCF服務(wù)框架 宿主(Host),即承載WCF Service運(yùn)行的環(huán)境。可用的宿主環(huán)境包括:(1) 自承載方式:在控制臺(tái)應(yīng)用程序與基于WinForm的應(yīng)用程序中都可以使用這種方式;(2) 系統(tǒng)服務(wù)方式:服務(wù)可以隨著操作系統(tǒng)的啟動(dòng)而自動(dòng)啟動(dòng);(3) IIS方式:與Web Services 的部署方式類似,由請(qǐng)求消息來(lái)激活服務(wù),但只支持HTTP方式的綁定;(4) WAS(Windows Pr

8、ocess Activation Service)方式:這個(gè)宿主是 IIS7 的一部分,只有 Windows Vista 和 Windows Server 2008提供默認(rèn)支持,它支持幾乎所有的通訊協(xié)議并提供了應(yīng)用程序池、循環(huán)回收、空閑管理、身份管理、隔離等強(qiáng)大的功能。 服務(wù)類(Service Class)是指一個(gè)標(biāo)記了一些WCF特有的屬性的類,它包含了對(duì)服務(wù)的業(yè)務(wù)邏輯的具體實(shí)現(xiàn)。端點(diǎn)(Endpoints)是WCF實(shí)現(xiàn)通信的核心要素,客戶端和服務(wù)端都通過(guò)端點(diǎn)來(lái)交換消息,WCF 允許我們?yōu)榉?wù)添加多個(gè)綁定和端點(diǎn)。端點(diǎn)由地址(Address)、綁定(Binding)以及契約(Contract)三部

9、分組成,如圖5所示。在WCF中,類ServiceEndpoint代表了一個(gè)Endpoint,在類中包含的EndpointAddress,Binding,ContractDescription類型分別對(duì)應(yīng)端點(diǎn)中的地址、綁定以及契約。 圖5 端點(diǎn)構(gòu)成圖 地址:每個(gè)服務(wù)都會(huì)關(guān)聯(lián)到一個(gè)唯一的地址,因此地址定位和唯一標(biāo)志了一個(gè)端點(diǎn),其主要提供了兩個(gè)重要信息: 服務(wù)位置以及傳送協(xié)議。在WCF 中,地址由System.ServiceModel.EndpointAddress對(duì)象來(lái)表示,其包括URI、Identity、Headers三個(gè)要素。綁定:綁定提供了一種可設(shè)置的方式來(lái)選擇傳輸協(xié)議、消息編碼、通訊模式、

10、可靠性、安全性、事務(wù)傳播以及交互方式等。例如在傳輸協(xié)議上可以選擇HTTP/HTTPS、TCP、P2P、IPC甚至是MSMQ等方式。消息編碼上可以選擇使用純文本方式來(lái)確保互操作能力,或者選擇二進(jìn)制編碼來(lái)優(yōu)化性能,或者使用 MTOM來(lái)提高負(fù)載能力,甚至是自定義編碼方式。 WCF中提供了BasicHttpBinding、NetTcpBinding、NetPeerTcpBinding、NetNamedPipeBinding、WSHttpBinding、WSFederationHttpBinding、WSDualHttpBinding 、NetMsmqBinding以及MsmqIntegrationBi

11、nding九種標(biāo)準(zhǔn)類型的綁定。 契約:契約是用來(lái)描述服務(wù)功能的一種平臺(tái)中立的標(biāo)準(zhǔn)方式,WCF 所有服務(wù)都需要實(shí)現(xiàn)一個(gè)或多個(gè)契約。WCF 定義了四種類型的契約: (1) 服務(wù)契約(Service Contracts): 定義了客戶端可以使用哪些服務(wù)操作。 (2) 數(shù)據(jù)契約(Data Contracts): 定義了服務(wù)傳輸?shù)臄?shù)據(jù)類型。WCF 定義了一些隱式數(shù)據(jù)契約,比如 int、string 等,但更多時(shí)候需要使用 DataContractAttribute 顯式定義那些自定義類型數(shù)據(jù)的數(shù)據(jù)契約。 (3) 錯(cuò)誤處理契約(Fault Contracts): 定義了服務(wù)引發(fā)的錯(cuò)誤信息,以及如何將這些異

12、常傳遞給客戶端。 (4) 消息契約(Message Contracts ): 允許直接操控服務(wù)的消息內(nèi)容和格式。 一般情況下,應(yīng)當(dāng)用接口來(lái)定義服務(wù)契約,盡管我們也可以使用類。將服務(wù)契約定義為接口基于如下幾點(diǎn)優(yōu)點(diǎn):(1) 便于契約的繼承,不同根的類型可以自由實(shí)現(xiàn)相同的契約; (2) 同一服務(wù)類型可以同時(shí)實(shí)現(xiàn)多個(gè)契約; (3) 類似于接口隔離原則,可以隨時(shí)修改服務(wù)類型的實(shí)現(xiàn)而不影響其它實(shí)現(xiàn); (4) 便于制定版本升級(jí)策略,新、舊版本的服務(wù)契約可以同時(shí)使用而互不影響。 在WCF中,對(duì)于自承載的服務(wù),端點(diǎn)的相關(guān)的信息可以有代碼實(shí)現(xiàn)與配置文件兩種定義方式。而對(duì)于IIS承載服務(wù),端點(diǎn)的相關(guān)的信息一般定義在

13、虛擬根目錄下的Web.Config文件中。一般來(lái)講,使用配置文件來(lái)定義端點(diǎn)相關(guān)信息是更為靈活、更為推薦的一種方式,其可以在不修改代碼、不重新發(fā)布系統(tǒng)的情況下對(duì)服務(wù)的地址、綁定和契約等參數(shù)進(jìn)行修改(因?yàn)樾薷腸onfig類型文件的內(nèi)容是不需要重新編譯和重新部署的)。 在下面的代碼中具體說(shuō)明了如何定義宿主端的端點(diǎn)相關(guān)信息。其中地址為http:/localhost:8080/HelloService,契約為WCFServiceHello命名空間下的IHello接口,綁定采用的是WSHttpBinding方式。值得注意的是,代碼中的HelloService為相對(duì)地址,http:/localhost:80

14、80/提供的是基址,當(dāng)然去掉基址直接將address設(shè)為http:/localhost:8080/HelloService也是可以的。代碼中還添加了名為MyServiceTypeBehaviors的行為配置,其將serviceMetadata節(jié)中的httpGetEnabled屬性設(shè)為了true,目的是為了自動(dòng)透過(guò) HTTP-GET發(fā)布服務(wù)的元數(shù)據(jù)。WCF提供的另外一種發(fā)布元數(shù)據(jù)的方式是使用專門的 MEX 端點(diǎn)。 在接下來(lái)的宿主代碼中,只需要簡(jiǎn)單的創(chuàng)建ServiceHost類型的對(duì)象,并利用其實(shí)例方法Open啟動(dòng)服務(wù)應(yīng)用程序即可,簡(jiǎn)要代碼如下所示:using(ServiceHost host=n

15、ew ServiceHost(typeof(WCFServiceHello.HelloWorld)Console.WriteLine(HelloService has been started.);host.Open();Console.ReadKey(); 客戶端和服務(wù)之間通過(guò)消息交換來(lái)完成方法調(diào)用和數(shù)據(jù)傳遞,而在 WCF 中定義了3種消息交換模式,如圖6所示。(1) OneWay:這種消息交換模式在調(diào)用方法后會(huì)立即返回而不需要等待服務(wù)端的消息返回。(2) Request/Reply:這種消息交換模式屬于同步調(diào)用。在調(diào)用服務(wù)方法后需要等待服務(wù)端的消息返回。(3) Duplex:這種消息交換模

16、式具有客戶端與服務(wù)端雙向通信的功能,同時(shí)它的實(shí)現(xiàn)還可以使消息交換具有異步回調(diào)的作用。圖6 WCF 中的3種消息交換模式 在設(shè)置完宿主端端點(diǎn)之后,同樣也必須為分布式應(yīng)用程序定義客戶端的端點(diǎn),而且只有當(dāng)客戶端的端點(diǎn)與宿主端的某個(gè)端點(diǎn)相互匹配時(shí),客戶端的請(qǐng)求才能被宿主端所監(jiān)聽(tīng)到。如果服務(wù)提供了發(fā)布元數(shù)據(jù),那么利用.NET Framework 3.0 SDK所提供的SvcUtil.exe工具可以很輕松的自動(dòng)生成與宿主端對(duì)應(yīng)的客戶端代理以及客戶端配置文件。比如,運(yùn)行宿主端應(yīng)用程序,然后打開(kāi)Visual Studio 2005命令提示符,鍵入SvcUtil http:/localhost:8080,便可以

17、在當(dāng)前目錄下得到客戶端代理文件HelloWorld.cs與客戶端配置文件output.config。另外一種簡(jiǎn)便直觀的可視化工具是SDK 所附帶的SvcConfigEditor.exe(C:Program FilesMicrosoft SDKsWindowsv6.0Abin目錄下,XP系統(tǒng)),使用這個(gè)工具可以非常方便地創(chuàng)建或修改宿主端和客戶端的配置文件。 生成好客戶端代理與配置文件后,在代碼中直接使用客戶端代理對(duì)象即可。using (HelloClient client = new HelloClient()client.Hello();Console.ReadKey(); 另外一種創(chuàng)建客戶端

18、代理的方式是使用ChannelFactory動(dòng)態(tài)的來(lái)創(chuàng)建。雖然WCF提供了這種方式,但是在實(shí)際開(kāi)發(fā)中并不推薦使用它,畢竟 ChannelFactory 直接依賴于契約,而這恰恰違背了 SOA 中邊界隔離的原則。利用服務(wù)器端與客戶端之間的Channel來(lái)創(chuàng)建客戶端代理的代碼舉例如下:ServiceEndpoint httpendpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(IHello), new WSHttpBinding(), new EndpointAddress(http:/localhost:808

19、0/HelloService);using (ChannelFactory factory =new ChannelFactory(httpendpoint)IHello service = factory.CreateChannel();service.Hello();Console.ReadKey();三、軟件的分析與設(shè)計(jì) 軟件主要的功能是初步實(shí)現(xiàn)基于WCF的局域網(wǎng)內(nèi)的實(shí)時(shí)通信,以面向服務(wù)為指導(dǎo)思想將具體開(kāi)發(fā)過(guò)程分為即時(shí)通信服務(wù)的設(shè)計(jì)與實(shí)現(xiàn)、宿主的設(shè)計(jì)與實(shí)現(xiàn)以及即時(shí)通信客戶端的設(shè)計(jì)與實(shí)現(xiàn)三部分,使得應(yīng)用程序具有較好的安全性、并發(fā)性、可擴(kuò)展性以及可維護(hù)性。1. 服務(wù)的設(shè)計(jì) 服務(wù)的設(shè)計(jì)包括了通

20、用模塊Common.dll的設(shè)計(jì)、服務(wù)契約IChat的定義與實(shí)現(xiàn)、客戶端的回調(diào)接口IChatCallback的定義三個(gè)部分。通用模塊Common.dll主要包括對(duì)聊天者類型Person的定義,其包括name(聊天者的名稱或代號(hào))以及ImageURL(聊天者選擇的頭像圖片的存儲(chǔ)路徑)兩個(gè)私有字段及相應(yīng)的屬性訪問(wèn)器。由于Person 類型是自定義數(shù)據(jù)類型,因此必須加上DataContractAttribute 來(lái)顯式定義它。Person類的實(shí)現(xiàn)核心代碼如下:DataContractpublic class Person private string imageURL;private string

21、name;public Person(string imageURL, string name)this.imageURL = imageURL; = name;DataMemberpublic string ImageURLget return imageURL; setimageURL = value;DataMemberpublic string Nameget return name; setname = value; 服務(wù)契約IChat的定義中主要包括Join、Say、Whisper以及Leave四個(gè)基本方法。其中Join表示:進(jìn)入聊天;Say表示向所有用戶廣播消息

22、;Whisper 表示對(duì)指定的用戶發(fā)送消息;Leave表示離開(kāi)聊天。 需添加ServiceContract屬性將IChat接口標(biāo)記為服務(wù)契約,由于要實(shí)現(xiàn)客戶端與服務(wù)端雙向通信的功能,因此還必須設(shè)置CallbackContract參數(shù),其參數(shù)IChatCallback為Duplex 模式下的客戶端回調(diào)類型。對(duì)于IChat接口中的方法來(lái)講,必須標(biāo)記OperationContract屬性。其中IsOneWay指示在該消息交換模式下調(diào)用方法后是否會(huì)立即返回而不需要等待服務(wù)端的消息返回;IsInitiating指示服務(wù)方法是否啟動(dòng)一個(gè) Session;IsTerminating指示服務(wù)方法調(diào)用完成是否結(jié)

23、束 Session。 IChat的定義代碼如下:ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IChatCallback)interface IChatOperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)void Say(string msg);OperationContract(IsOneWay = true, IsInitiating = false, IsTerminatin

24、g = false)void Whisper(string to, string msg);OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)Person Join(Person name);OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)void Leave(); 客戶端的回調(diào)接口IChatCallback的定義包括了分別對(duì)應(yīng)Join、Say、Whisper以及Leave的Use

25、rEnter、Receive、ReceiveWhisper以及UserLeave四個(gè)基本方法。UserEnter表示當(dāng)有新聊天用戶加入時(shí)所有聊天用戶接收到一個(gè)相應(yīng)的通知;Receive表示接收用戶廣播的消息;ReceiveWhisper 表示接收相關(guān)用戶發(fā)來(lái)的消息;UserLeave表示當(dāng)有聊天用戶離開(kāi)時(shí)所有聊天用戶接收一個(gè)相應(yīng)的通知。 注意在接口定義中,每個(gè)服務(wù)方法的消息轉(zhuǎn)換模式均設(shè)置為One-Way。此外,回調(diào)接口是被本地調(diào)用,因此不需要定義ServiceContract屬性。 回調(diào)接口IChatCallback的定義的代碼如下:interface IChatCallbackOperati

26、onContract(IsOneWay = true)void Receive(Person sender, string message);OperationContract(IsOneWay = true)void ReceiveWhisper(Person sender, string message);OperationContract(IsOneWay = true)void UserEnter(Person person);OperationContract(IsOneWay = true)void UserLeave(Person person); 定義了服務(wù)契約以及客戶端回調(diào)接

27、口之后,仍需要定義一些消息類型用作客戶端與服務(wù)之間的交互,具體代碼及解釋如下:/ 定義說(shuō)明消息類型的枚舉類型MessageTypepublic enum MessageType Receive, UserEnter, UserLeave, ReceiveWhisper ;/ ChatEventArgs繼承至EventArgs,作為傳遞的消息參數(shù),/其包括消息的類型msgType、消息發(fā)送者person以及消息的主體內(nèi)容messagepublic class ChatEventArgs : EventArgspublic MessageType msgType;public Person per

28、son;public string message; 最后是對(duì)服務(wù)契約中IChat接口的實(shí)現(xiàn)類ChatService的設(shè)計(jì)。值得注意的是ChatService繼承了IChat,不再需要像服務(wù)契約IChat一樣添加ServiceContract屬性。另外設(shè)置了其ServiceBehavior屬性的InstanceContextMode參數(shù)來(lái)決定實(shí)例化方式(PerSession方式或者PerCall方式)。PerSession表明服務(wù)對(duì)象的生命周期存活于一個(gè)會(huì)話期間,在同一個(gè)會(huì)話期間對(duì)于服務(wù)的不同操作的調(diào)用都會(huì)施加到同一個(gè)客戶端代理類型的對(duì)象上;PerCall則表示服務(wù)對(duì)象是在方法被調(diào)用時(shí)創(chuàng)建,結(jié)

29、束后即被銷毀。ServiceBehavior.ConcurrencyMode 參數(shù)的設(shè)置則用于控制具體服務(wù)對(duì)象的并發(fā)行為,其包括三種行為:Single: 為默認(rèn)方式。服務(wù)實(shí)例是單線程的,不接受重入調(diào)用(reentrant calls)。也就是說(shuō)對(duì)于同一個(gè)服務(wù)實(shí)例的多個(gè)調(diào)用必須排隊(duì),直到上一次調(diào)用完成后才能繼續(xù)。 Reentrant: 和 Single 一樣,也是單線程的,但能接受重入調(diào)用,至于針對(duì)同一服務(wù)對(duì)象的多個(gè)調(diào)用仍然需要排隊(duì)。因?yàn)樵?Single 模式下,當(dāng)方法調(diào)用另外一個(gè)服務(wù)時(shí),方法會(huì)阻塞,直到所調(diào)用的服務(wù)完成。如果方法不能重入,那么調(diào)用方會(huì)因無(wú)法接受所調(diào)用服務(wù)的返回消息,無(wú)法解除阻塞

30、狀態(tài)而陷入死鎖。Reentrant 模式解決了這種不足。 Multiple: 允許多個(gè)客戶端同時(shí)調(diào)用服務(wù)方法。不再有鎖的問(wèn)題,不再提供同步保障。因此使用該模式時(shí),必須自行提供多線程同步機(jī)制(比如在本例中給static類型的syncObj對(duì)象加鎖)來(lái)保證數(shù)據(jù)成員的讀寫安全。類ChatService的實(shí)現(xiàn)代碼及解析如下:ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,ConcurrencyMode = ConcurrencyMode.Multiple)public class ChatService : IC

31、hat/用于保障多線程同步而設(shè)置的對(duì)象private static Object syncObj = new Object();/客戶端的回調(diào)接口對(duì)象IChatCallback callback = null;/用于廣播事件的委托public delegate void ChatEventHandler(object sender, ChatEventArgs e);public static event ChatEventHandler ChatEvent;private ChatEventHandler myEventHandler = null;/利用字典對(duì)象chatters來(lái)保存聊天者

32、對(duì)象以及綁定的對(duì)應(yīng)事件委托static Dictionary chatters =new Dictionary();/當(dāng)前聊天者對(duì)象private Person person;/ 判斷具有相應(yīng)名字的聊天者是否存在于字典對(duì)象中private bool checkIfPersonExists(string name)foreach (Person p in chatters.Keys)if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)return true;return false;/ 在字典對(duì)象中搜索判斷其中是否包含了相應(yīng)名字的

33、聊天者,/ 如果有則返回其對(duì)應(yīng)的委托ChatEventHandler;否則返回空private ChatEventHandler getPersonHandler(string name)foreach (Person p in chatters.Keys)/不區(qū)分大小寫if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)ChatEventHandler chatTo = null;chatters.TryGetValue(p, out chatTo);return chatTo;return null;/ 在字典對(duì)象中搜索判斷

34、其中是否包含了相應(yīng)名字的聊天者,/ 如果有則返回聊天者對(duì)象;否則返回空private Person getPerson(string name)foreach (Person p in chatters.Keys) if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)return p;return null;/ 加入聊天,如果字典對(duì)象chatters中沒(méi)有同名的聊天者public Person Join(Person person) bool userAdded = false;/創(chuàng)建新的ChatEventHandler類型委托

35、,其指向MyEventHandler()方法myEventHandler = new ChatEventHandler(MyEventHandler);/執(zhí)行關(guān)鍵區(qū)域,判斷是否存在同名聊天者,/如果不存在則向字典中添加該對(duì)象并將MyEventHandler委托作為其value,/以待后面來(lái)觸發(fā)lock (syncObj)if (!checkIfPersonExists(person.Name) & person != null)this.person = person;chatters.Add(person, MyEventHandler);userAdded = true;/如果新的聊天者添

36、加成功,獲得一個(gè)回調(diào)的實(shí)例來(lái)創(chuàng)建一個(gè)消息,/并將其廣播給其他的聊天者/讀取字典chatters的所有聊天者對(duì)象并返回一個(gè)包含了所有對(duì)象的列表if (userAdded)callback = OperationContext.Current.GetCallbackChannel();ChatEventArgs e = new ChatEventArgs();e.msgType = MessageType.UserEnter;e.person = this.person;/廣播有新聊天者加入的消息BroadcastMessage(e);/將新加入聊天者對(duì)象的委托加到全局的多播委托上ChatEven

37、t += myEventHandler;Person list = new Personchatters.Count;/執(zhí)行關(guān)鍵區(qū)域,將字典chatters的所有聊天者對(duì)象/拷貝至一個(gè)聊天者列表上,用于方法返回lock (syncObj)chatters.Keys.CopyTo(list, 0);return list;elsereturn null;/ 廣播當(dāng)前聊天者輸入的消息給所有聊天者public void Say(string msg)ChatEventArgs e = new ChatEventArgs();e.msgType = MessageType.Receive;e.pers

38、on = this.person;e.message = msg;BroadcastMessage(e);/在字典對(duì)象chatters中查找具有相應(yīng)名稱的聊天者對(duì)象,/并異步地觸發(fā)其對(duì)應(yīng)的ChatEventHandle委托public void Whisper(string to, string msg)ChatEventArgs e = new ChatEventArgs();e.msgType = MessageType.ReceiveWhisper;e.person = this.person;e.message = msg;tryChatEventHandler chatterTo;/

39、執(zhí)行關(guān)鍵區(qū)域,獲取具有相應(yīng)名稱的聊天者對(duì)象在chatters中所對(duì)應(yīng)的委托l(wèi)ock (syncObj)chatterTo = getPersonHandler(to);if (chatterTo = null)throw new KeyNotFoundException(The person whos name is + to + could not be found); /異步執(zhí)行委托chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);catch (KeyNotFoundException)/ 當(dāng)聊天者離開(kāi)時(shí),從

40、字典chatters中移除包含該對(duì)象的項(xiàng)。/ 并從全局的多播委托上移除該對(duì)象對(duì)應(yīng)的委托public void Leave()if (this.person = null)return;/獲得該聊天者對(duì)象所對(duì)應(yīng)的ChatEventHandler委托ChatEventHandler chatterToRemove = getPersonHandler(this.person.Name);/執(zhí)行關(guān)鍵區(qū)域,從從字典chatters中移除包含該聊天者對(duì)象的項(xiàng)。lock (syncObj)chatters.Remove(this.person);/從全局的多播委托上移除該對(duì)象對(duì)應(yīng)的委托ChatEvent

41、-= chatterToRemove;ChatEventArgs e = new ChatEventArgs();e.msgType = MessageType.UserLeave;e.person = this.person;this.person = null;/將消息廣播給其他聊天者BroadcastMessage(e);/ 當(dāng)chatters中的聊天者對(duì)象所對(duì)應(yīng)的ChatEventHandler委托被觸發(fā)時(shí),/ MyEventHandler方法將執(zhí)行。/ 該方法通過(guò)檢查傳遞過(guò)來(lái)的ChatEventArgs參數(shù)類型來(lái)執(zhí)行相應(yīng)的客戶端/的回調(diào)接口中的方法。private void MyEv

42、entHandler(object sender, ChatEventArgs e)tryswitch (e.msgType)case MessageType.Receive:callback.Receive(e.person, e.message);break;case MessageType.ReceiveWhisper:callback.ReceiveWhisper(e.person, e.message);break;case MessageType.UserEnter:callback.UserEnter(e.person);break;case MessageType.UserLe

43、ave:callback.UserLeave(e.person);break;catchLeave();/ 異步地觸發(fā)字典chatters中所有聊天者對(duì)象所對(duì)應(yīng)的ChatEventHandler委托。/ BeginInvoke 方法可啟動(dòng)異步調(diào)用。第一個(gè)參數(shù)是一個(gè) AsyncCallback 委托,/該委托引用在異步調(diào)用完成時(shí)要調(diào)用的方法。/ 第二個(gè)參數(shù)是一個(gè)用戶定義的對(duì)象,該對(duì)象可向回調(diào)方法傳遞信息。/BeginInvoke 立即返回,不等待異步調(diào)用完成。/BeginInvoke 會(huì)返回 IAsyncResult,這個(gè)結(jié)果可用于監(jiān)視異步調(diào)用進(jìn)度。private void BroadcastM

44、essage(ChatEventArgs e)ChatEventHandler temp = ChatEvent;if (temp != null)foreach (ChatEventHandler handler in temp.GetInvocationList()handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);/ EndInvoke 方法檢索異步調(diào)用的結(jié)果。/調(diào)用 BeginInvoke 后可隨時(shí)調(diào)用 EndInvoke 方法;/如果異步調(diào)用尚未完成,EndInvoke 將一直阻止調(diào)用線程,/直到異步調(diào)用完成

45、后才允許調(diào)用線程執(zhí)行。private void EndAsync(IAsyncResult ar)ChatEventHandler d = null;try/System.Runtime.Remoting.Messaging.AsyncResult封裝了異步委托上/的異步操作的結(jié)果。/AsyncResult.AsyncDelegate 屬性可用于獲取在其上調(diào)用/異步調(diào)用的委托對(duì)象。 System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messaging.AsyncResult)ar;d =

46、 (ChatEventHandler)asres.AsyncDelegate);d.EndInvoke(ar);catchChatEvent -= d;2. 宿主端的設(shè)計(jì) 設(shè)計(jì)好服務(wù)之后,宿主端的設(shè)計(jì)相對(duì)簡(jiǎn)單,只是一個(gè)基于控制臺(tái)的應(yīng)用程序。首先建立一個(gè)ServiceHost類型的對(duì)象host,然后調(diào)用ServiceHost類型的實(shí)例方法Open即可啟動(dòng)宿主監(jiān)聽(tīng)。需要注意的是在程序末尾應(yīng)當(dāng)調(diào)用Abort以及Close將資源釋放掉,或者利用using語(yǔ)句來(lái)自動(dòng)釋放所占用的資源。下面是宿主端的實(shí)現(xiàn)代碼以及配置文件內(nèi)容:static void Main(string args)Uri uri = ne

47、w Uri(ConfigurationManager.AppSettingsaddr);ServiceHost host = new ServiceHost(typeof(Chatters.ChatService), uri);host.Open();Console.WriteLine(Chat service Host now listening on Endpoint 0, uri.ToString();Console.WriteLine(Press ENTER to stop chat service.);Console.ReadLine();host.Abort();host.Clos

48、e(); 配置文件中,地址為net.tcp:/localhost:10001/chatservice;綁定為netTcpBinding,并且提供可靠性傳輸、不保障安全性,發(fā)送超時(shí)時(shí)間限定為1秒;契約為Chatters.IChat接口。另外設(shè)置了名稱為DuplexBinding的綁定配置,其對(duì)服務(wù)的分流作了限制,并發(fā)的Session個(gè)數(shù)的上限為10000。 3. 客戶端的設(shè)計(jì) 客戶端的設(shè)計(jì)主要包括實(shí)現(xiàn)客戶端代理與配置文件、客戶端的回調(diào)接口契約的實(shí)現(xiàn)以及客戶端登錄、主窗體、聊天窗體界面的設(shè)計(jì)與代碼邏輯實(shí)現(xiàn)這三部分。表示客戶端的各個(gè)對(duì)象之間行為關(guān)系的序列圖如圖7所示。圖7 客戶端應(yīng)用的序列圖 首先是

49、利用svcutil.exe工具自動(dòng)生成的客戶端代理以及客戶端的配置文件,并做出相應(yīng)的更改。打開(kāi)Visual Studio 2005 命令提示,轉(zhuǎn)到ChatService所在目錄,依次輸入以下命令可以生成同步的客戶端代理。svcutil ChatService.exesvcutil *.wsdl *.xsd /language:C# /out:MyProxy.cs /config:app.config 輸入以下命令可以創(chuàng)建異步的客戶端代理:svcutil *.wsdl *.xsd /a /language:C# /out:MyProxy2.cs /config:app.config 因?yàn)橛脩艏尤?/p>

50、聊天這個(gè)過(guò)程是異步執(zhí)行的,所以可以利用自動(dòng)生成的異步客戶端代理文件中的BeginJoin 與EndJoin 方法來(lái)替換同步的Join方法。 客戶端代理的實(shí)現(xiàn)代碼如下所示:using Common;System.CodeDom.Compiler.GeneratedCodeAttribute(System.ServiceModel, )System.ServiceModel.ServiceContractAttribute(CallbackContract = typeof(IChatCallback), SessionMode = System.ServiceModel.Sessi

51、onMode.Required)public interface IChatSystem.ServiceModel.OperationContractAttribute(AsyncPattern = true, Action = /IChat/Join, ReplyAction = /IChat/JoinResponse)System.IAsyncResult BeginJoin(Person name, System.AsyncCallback callback, object asyncState);Person EndJoin(System.IAsyncResult result);System.ServiceModel.OperationContractAttribute(IsOne

溫馨提示

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

最新文檔

評(píng)論

0/150

提交評(píng)論