




已閱讀5頁,還剩29頁未讀, 繼續(xù)免費閱讀
版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
基于WCF的即時通訊軟件的設計與實現(xiàn) 摘要: 介紹了Microsoft用于構建分布式面向服務架構系統(tǒng)的新一代框架WCF的體系結構與技術要素,并通過開發(fā)一套即時通信軟件展現(xiàn)了基于WCF構架開發(fā)分布式應用程序的編程步驟與技巧。關鍵詞:WCF;SOA;分布式;即時通信 一、前言 自從在微軟提出.NET戰(zhàn)略以來,就針對建立企業(yè)級的分布式應用先后推出了一系列產品和技術,包括:ASP.NET Web服務、.NET Remoting、Message Queuing以及Enterprise Service等。這些技術為基于微軟技術的軟件研發(fā)人員開發(fā)分布式應用提供了很大的便利,同時也各自存在著一些不足。WCF(Windows Communication Foundation)作為微軟基于SOA所推出的.NET平臺下的新一代框架產品集成了現(xiàn)有技術的優(yōu)點,代表了未來軟件架構設計與開發(fā)的發(fā)展方向。因此,掌握并能在未來應用中合理運用WCF技術,對于程序員特別是基于微軟技術開發(fā)的程序員而言是十分必要的?;诖?,文章通過介紹一套即時通信軟件的具體開發(fā)過程來展現(xiàn)基于WCF技術的分布式軟件研發(fā)的基本步驟與高級技巧。二、WCF概述 對于一個好的分布式系統(tǒng)來講,設計時應當考慮到異構性、開放性、安全性、可擴展性、故障處理、并發(fā)性以及透明性等問題。基于SOAP的Web Service可以實現(xiàn)異構環(huán)境的互操作性,保證了跨平臺的通信。利用WSE(Web Service Enhancements)可以為ASMX提供安全性的保證。.NET Remoting具有豐富的擴展功能,可以創(chuàng)建定制的信道、格式化器和代理程序。Enterprise Service(COM+)提供了對事務的支持,其中還包括分布式事務,可實現(xiàn)故障的恢復。MSMQ可以支持異步調用、脫機連接、斷點連接等功能,利用消息隊列支持應用程序之間的消息傳遞。從功能角度來看,WCF整合了ASMX、.Net Remoting、Enterprise Service、WSE以及MSMQ等現(xiàn)有技術的優(yōu)點,它提供了一種構建安全可靠的分布式面向服務系統(tǒng)的統(tǒng)一的框架模型,使軟件研發(fā)人員在開發(fā)分布式應用時變得更加輕松。1. 面向服務 既然WCF是一套面向服務的框架,服務自然便是WCF中最為重要的概念。服務是指暴露在外的一系列功能的集合,面向服務則是指一套構建“面向服務程序”的抽象原則以及最優(yōu)方法。對于業(yè)務邏輯的理解,傳統(tǒng)的編程方式認為應將業(yè)務邏輯封裝為對象,該對象提供了與業(yè)務相關的一些功能;而基于WCF的程序設計卻更多的是考慮如何提供服務以及消費服務。與面向組件服務程序類似,基于SOA 的應用程序將服務封裝到了單個邏輯程序當中,如圖1所示。圖1封裝服務的SOA應用程序邏輯圖2. WCF體系結構 WCF擁有一個非常靈活的分層體系結構,分布式應用程序可以使用高級API或者低級API編寫。高級API或者服務層可以用于調用方法和事件。服務層把這些高級的抽象代碼轉換為消息,以使用低級API上的信道和端口。圖2中顯示了WCF應用程序的各個層。圖2 WCF體系結構圖 WCF提供了對可靠性、事務性、并發(fā)管理、安全性以及實例激活等技術的有力支持,而這些支持均依賴于如圖3所示的WCF構架。 在客戶端,分布式應用通過一個代理來轉發(fā)對宿主端所提供服務的調用,而代理擁有和服務相同的操作接口,另外還有一些附加的代理管理方法。這也就意味著客戶端從來不會直接調用服務,即便這個服務就在本機的內存中。當客戶端代理接收到來自客戶端的調用請求后,它將消息通過信道鏈向下傳遞。每個信道都會執(zhí)行相應的消息的調用前處理,例如對消息的編碼、提供可靠的會話、對消息進行加密等??蛻舳说淖詈笠粋€信道則是傳輸信道,根據(jù)配置的傳輸方式發(fā)送消息給宿主。 在宿主端,消息同樣通過信道鏈進行傳輸。與客戶端信道相對應,宿主端信道也會對消息執(zhí)行相應的宿主端的調用前處理,例如對消息的解碼、提供會話管理、對消息進行解密等。宿主端的最后一個信道則負責將消息發(fā)送給消息分發(fā)器(Dispatcher),由分發(fā)器負責調用服務的實例。圖3 WCF構架示意圖3. WCF的基本技術要素 作為基于SOA的一個框架產品,WCF實際上是構建了一個在互聯(lián)系統(tǒng)中實現(xiàn)各個應用程序之間通信的分布式框架。它使得系統(tǒng)構架師與開發(fā)人員在構建分布式系統(tǒng)時,能將更多的精力投入到與系統(tǒng)的業(yè)務邏輯本身的設計上來,而無需過多的考慮底層通信的實現(xiàn)及相關問題。WCF最核心的部分是能夠快捷的創(chuàng)建一個服務,一個WCF服務端框架由宿主、端點以及服務類三部分所組成,如圖4所示。圖4 WCF服務框架 宿主(Host),即承載WCF Service運行的環(huán)境??捎玫乃拗鳝h(huán)境包括:(1) 自承載方式:在控制臺應用程序與基于WinForm的應用程序中都可以使用這種方式;(2) 系統(tǒng)服務方式:服務可以隨著操作系統(tǒng)的啟動而自動啟動;(3) IIS方式:與Web Services 的部署方式類似,由請求消息來激活服務,但只支持HTTP方式的綁定;(4) WAS(Windows Process Activation Service)方式:這個宿主是 IIS7 的一部分,只有 Windows Vista 和 Windows Server 2008提供默認支持,它支持幾乎所有的通訊協(xié)議并提供了應用程序池、循環(huán)回收、空閑管理、身份管理、隔離等強大的功能。 服務類(Service Class)是指一個標記了一些WCF特有的屬性的類,它包含了對服務的業(yè)務邏輯的具體實現(xiàn)。端點(Endpoints)是WCF實現(xiàn)通信的核心要素,客戶端和服務端都通過端點來交換消息,WCF 允許我們?yōu)榉仗砑佣鄠€綁定和端點。端點由地址(Address)、綁定(Binding)以及契約(Contract)三部分組成,如圖5所示。在WCF中,類ServiceEndpoint代表了一個Endpoint,在類中包含的EndpointAddress,Binding,ContractDescription類型分別對應端點中的地址、綁定以及契約。 圖5 端點構成圖 地址:每個服務都會關聯(lián)到一個唯一的地址,因此地址定位和唯一標志了一個端點,其主要提供了兩個重要信息: 服務位置以及傳送協(xié)議。在WCF 中,地址由System.ServiceModel.EndpointAddress對象來表示,其包括URI、Identity、Headers三個要素。綁定:綁定提供了一種可設置的方式來選擇傳輸協(xié)議、消息編碼、通訊模式、可靠性、安全性、事務傳播以及交互方式等。例如在傳輸協(xié)議上可以選擇HTTP/HTTPS、TCP、P2P、IPC甚至是MSMQ等方式。消息編碼上可以選擇使用純文本方式來確保互操作能力,或者選擇二進制編碼來優(yōu)化性能,或者使用 MTOM來提高負載能力,甚至是自定義編碼方式。 WCF中提供了BasicHttpBinding、NetTcpBinding、NetPeerTcpBinding、NetNamedPipeBinding、WSHttpBinding、WSFederationHttpBinding、WSDualHttpBinding 、NetMsmqBinding以及MsmqIntegrationBinding九種標準類型的綁定。 契約:契約是用來描述服務功能的一種平臺中立的標準方式,WCF 所有服務都需要實現(xiàn)一個或多個契約。WCF 定義了四種類型的契約: (1) 服務契約(Service Contracts): 定義了客戶端可以使用哪些服務操作。 (2) 數(shù)據(jù)契約(Data Contracts): 定義了服務傳輸?shù)臄?shù)據(jù)類型。WCF 定義了一些隱式數(shù)據(jù)契約,比如 int、string 等,但更多時候需要使用 DataContractAttribute 顯式定義那些自定義類型數(shù)據(jù)的數(shù)據(jù)契約。 (3) 錯誤處理契約(Fault Contracts): 定義了服務引發(fā)的錯誤信息,以及如何將這些異常傳遞給客戶端。 (4) 消息契約(Message Contracts ): 允許直接操控服務的消息內容和格式。 一般情況下,應當用接口來定義服務契約,盡管我們也可以使用類。將服務契約定義為接口基于如下幾點優(yōu)點:(1) 便于契約的繼承,不同根的類型可以自由實現(xiàn)相同的契約; (2) 同一服務類型可以同時實現(xiàn)多個契約; (3) 類似于接口隔離原則,可以隨時修改服務類型的實現(xiàn)而不影響其它實現(xiàn); (4) 便于制定版本升級策略,新、舊版本的服務契約可以同時使用而互不影響。 在WCF中,對于自承載的服務,端點的相關的信息可以有代碼實現(xiàn)與配置文件兩種定義方式。而對于IIS承載服務,端點的相關的信息一般定義在虛擬根目錄下的Web.Config文件中。一般來講,使用配置文件來定義端點相關信息是更為靈活、更為推薦的一種方式,其可以在不修改代碼、不重新發(fā)布系統(tǒng)的情況下對服務的地址、綁定和契約等參數(shù)進行修改(因為修改config類型文件的內容是不需要重新編譯和重新部署的)。 在下面的代碼中具體說明了如何定義宿主端的端點相關信息。其中地址為http:/localhost:8080/HelloService,契約為WCFServiceHello命名空間下的IHello接口,綁定采用的是WSHttpBinding方式。值得注意的是,代碼中的HelloService為相對地址,http:/localhost:8080/提供的是基址,當然去掉基址直接將address設為http:/localhost:8080/HelloService也是可以的。代碼中還添加了名為MyServiceTypeBehaviors的行為配置,其將serviceMetadata節(jié)中的httpGetEnabled屬性設為了true,目的是為了自動透過 HTTP-GET發(fā)布服務的元數(shù)據(jù)。WCF提供的另外一種發(fā)布元數(shù)據(jù)的方式是使用專門的 MEX 端點。 在接下來的宿主代碼中,只需要簡單的創(chuàng)建ServiceHost類型的對象,并利用其實例方法Open啟動服務應用程序即可,簡要代碼如下所示:using(ServiceHost host=new ServiceHost(typeof(WCFServiceHello.HelloWorld)Console.WriteLine(HelloService has been started.);host.Open();Console.ReadKey(); 客戶端和服務之間通過消息交換來完成方法調用和數(shù)據(jù)傳遞,而在 WCF 中定義了3種消息交換模式,如圖6所示。(1) OneWay:這種消息交換模式在調用方法后會立即返回而不需要等待服務端的消息返回。(2) Request/Reply:這種消息交換模式屬于同步調用。在調用服務方法后需要等待服務端的消息返回。(3) Duplex:這種消息交換模式具有客戶端與服務端雙向通信的功能,同時它的實現(xiàn)還可以使消息交換具有異步回調的作用。圖6 WCF 中的3種消息交換模式 在設置完宿主端端點之后,同樣也必須為分布式應用程序定義客戶端的端點,而且只有當客戶端的端點與宿主端的某個端點相互匹配時,客戶端的請求才能被宿主端所監(jiān)聽到。如果服務提供了發(fā)布元數(shù)據(jù),那么利用.NET Framework 3.0 SDK所提供的SvcUtil.exe工具可以很輕松的自動生成與宿主端對應的客戶端代理以及客戶端配置文件。比如,運行宿主端應用程序,然后打開Visual Studio 2005命令提示符,鍵入SvcUtil http:/localhost:8080,便可以在當前目錄下得到客戶端代理文件HelloWorld.cs與客戶端配置文件output.config。另外一種簡便直觀的可視化工具是SDK 所附帶的SvcConfigEditor.exe(C:Program FilesMicrosoft SDKsWindowsv6.0Abin目錄下,XP系統(tǒng)),使用這個工具可以非常方便地創(chuàng)建或修改宿主端和客戶端的配置文件。 生成好客戶端代理與配置文件后,在代碼中直接使用客戶端代理對象即可。using (HelloClient client = new HelloClient()client.Hello();Console.ReadKey(); 另外一種創(chuàng)建客戶端代理的方式是使用ChannelFactory動態(tài)的來創(chuàng)建。雖然WCF提供了這種方式,但是在實際開發(fā)中并不推薦使用它,畢竟 ChannelFactory 直接依賴于契約,而這恰恰違背了 SOA 中邊界隔離的原則。利用服務器端與客戶端之間的Channel來創(chuàng)建客戶端代理的代碼舉例如下:ServiceEndpoint httpendpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(IHello), new WSHttpBinding(), new EndpointAddress(http:/localhost:8080/HelloService);using (ChannelFactory factory =new ChannelFactory(httpendpoint)IHello service = factory.CreateChannel();service.Hello();Console.ReadKey();三、軟件的分析與設計 軟件主要的功能是初步實現(xiàn)基于WCF的局域網(wǎng)內的實時通信,以面向服務為指導思想將具體開發(fā)過程分為即時通信服務的設計與實現(xiàn)、宿主的設計與實現(xiàn)以及即時通信客戶端的設計與實現(xiàn)三部分,使得應用程序具有較好的安全性、并發(fā)性、可擴展性以及可維護性。1. 服務的設計 服務的設計包括了通用模塊Common.dll的設計、服務契約IChat的定義與實現(xiàn)、客戶端的回調接口IChatCallback的定義三個部分。通用模塊Common.dll主要包括對聊天者類型Person的定義,其包括name(聊天者的名稱或代號)以及ImageURL(聊天者選擇的頭像圖片的存儲路徑)兩個私有字段及相應的屬性訪問器。由于Person 類型是自定義數(shù)據(jù)類型,因此必須加上DataContractAttribute 來顯式定義它。Person類的實現(xiàn)核心代碼如下:DataContractpublic class Person private string imageURL;private string 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; 服務契約IChat的定義中主要包括Join、Say、Whisper以及Leave四個基本方法。其中Join表示:進入聊天;Say表示向所有用戶廣播消息;Whisper 表示對指定的用戶發(fā)送消息;Leave表示離開聊天。 需添加ServiceContract屬性將IChat接口標記為服務契約,由于要實現(xiàn)客戶端與服務端雙向通信的功能,因此還必須設置CallbackContract參數(shù),其參數(shù)IChatCallback為Duplex 模式下的客戶端回調類型。對于IChat接口中的方法來講,必須標記OperationContract屬性。其中IsOneWay指示在該消息交換模式下調用方法后是否會立即返回而不需要等待服務端的消息返回;IsInitiating指示服務方法是否啟動一個 Session;IsTerminating指示服務方法調用完成是否結束 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, IsTerminating = 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(); 客戶端的回調接口IChatCallback的定義包括了分別對應Join、Say、Whisper以及Leave的UserEnter、Receive、ReceiveWhisper以及UserLeave四個基本方法。UserEnter表示當有新聊天用戶加入時所有聊天用戶接收到一個相應的通知;Receive表示接收用戶廣播的消息;ReceiveWhisper 表示接收相關用戶發(fā)來的消息;UserLeave表示當有聊天用戶離開時所有聊天用戶接收一個相應的通知。 注意在接口定義中,每個服務方法的消息轉換模式均設置為One-Way。此外,回調接口是被本地調用,因此不需要定義ServiceContract屬性。 回調接口IChatCallback的定義的代碼如下:interface IChatCallbackOperationContract(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); 定義了服務契約以及客戶端回調接口之后,仍需要定義一些消息類型用作客戶端與服務之間的交互,具體代碼及解釋如下:/ 定義說明消息類型的枚舉類型MessageTypepublic enum MessageType Receive, UserEnter, UserLeave, ReceiveWhisper ;/ ChatEventArgs繼承至EventArgs,作為傳遞的消息參數(shù),/其包括消息的類型msgType、消息發(fā)送者person以及消息的主體內容messagepublic class ChatEventArgs : EventArgspublic MessageType msgType;public Person person;public string message; 最后是對服務契約中IChat接口的實現(xiàn)類ChatService的設計。值得注意的是ChatService繼承了IChat,不再需要像服務契約IChat一樣添加ServiceContract屬性。另外設置了其ServiceBehavior屬性的InstanceContextMode參數(shù)來決定實例化方式(PerSession方式或者PerCall方式)。PerSession表明服務對象的生命周期存活于一個會話期間,在同一個會話期間對于服務的不同操作的調用都會施加到同一個客戶端代理類型的對象上;PerCall則表示服務對象是在方法被調用時創(chuàng)建,結束后即被銷毀。ServiceBehavior.ConcurrencyMode 參數(shù)的設置則用于控制具體服務對象的并發(fā)行為,其包括三種行為:Single: 為默認方式。服務實例是單線程的,不接受重入調用(reentrant calls)。也就是說對于同一個服務實例的多個調用必須排隊,直到上一次調用完成后才能繼續(xù)。 Reentrant: 和 Single 一樣,也是單線程的,但能接受重入調用,至于針對同一服務對象的多個調用仍然需要排隊。因為在 Single 模式下,當方法調用另外一個服務時,方法會阻塞,直到所調用的服務完成。如果方法不能重入,那么調用方會因無法接受所調用服務的返回消息,無法解除阻塞狀態(tài)而陷入死鎖。Reentrant 模式解決了這種不足。 Multiple: 允許多個客戶端同時調用服務方法。不再有鎖的問題,不再提供同步保障。因此使用該模式時,必須自行提供多線程同步機制(比如在本例中給static類型的syncObj對象加鎖)來保證數(shù)據(jù)成員的讀寫安全。類ChatService的實現(xiàn)代碼及解析如下:ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,ConcurrencyMode = ConcurrencyMode.Multiple)public class ChatService : IChat/用于保障多線程同步而設置的對象private static Object syncObj = new Object();/客戶端的回調接口對象IChatCallback callback = null;/用于廣播事件的委托public delegate void ChatEventHandler(object sender, ChatEventArgs e);public static event ChatEventHandler ChatEvent;private ChatEventHandler myEventHandler = null;/利用字典對象chatters來保存聊天者對象以及綁定的對應事件委托static Dictionary chatters =new Dictionary();/當前聊天者對象private Person person;/ 判斷具有相應名字的聊天者是否存在于字典對象中private bool checkIfPersonExists(string name)foreach (Person p in chatters.Keys)if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)return true;return false;/ 在字典對象中搜索判斷其中是否包含了相應名字的聊天者,/ 如果有則返回其對應的委托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;/ 在字典對象中搜索判斷其中是否包含了相應名字的聊天者,/ 如果有則返回聊天者對象;否則返回空private Person getPerson(string name)foreach (Person p in chatters.Keys) if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)return p;return null;/ 加入聊天,如果字典對象chatters中沒有同名的聊天者public Person Join(Person person) bool userAdded = false;/創(chuàng)建新的ChatEventHandler類型委托,其指向MyEventHandler()方法myEventHandler = new ChatEventHandler(MyEventHandler);/執(zhí)行關鍵區(qū)域,判斷是否存在同名聊天者,/如果不存在則向字典中添加該對象并將MyEventHandler委托作為其value,/以待后面來觸發(fā)lock (syncObj)if (!checkIfPersonExists(person.Name) & person != null)this.person = person;chatters.Add(person, MyEventHandler);userAdded = true;/如果新的聊天者添加成功,獲得一個回調的實例來創(chuàng)建一個消息,/并將其廣播給其他的聊天者/讀取字典chatters的所有聊天者對象并返回一個包含了所有對象的列表if (userAdded)callback = OperationContext.Current.GetCallbackChannel();ChatEventArgs e = new ChatEventArgs();e.msgType = MessageType.UserEnter;e.person = this.person;/廣播有新聊天者加入的消息BroadcastMessage(e);/將新加入聊天者對象的委托加到全局的多播委托上ChatEvent += myEventHandler;Person list = new Personchatters.Count;/執(zhí)行關鍵區(qū)域,將字典chatters的所有聊天者對象/拷貝至一個聊天者列表上,用于方法返回lock (syncObj)chatters.Keys.CopyTo(list, 0);return list;elsereturn null;/ 廣播當前聊天者輸入的消息給所有聊天者public void Say(string msg)ChatEventArgs e = new ChatEventArgs();e.msgType = MessageType.Receive;e.person = this.person;e.message = msg;BroadcastMessage(e);/在字典對象chatters中查找具有相應名稱的聊天者對象,/并異步地觸發(fā)其對應的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;/執(zhí)行關鍵區(qū)域,獲取具有相應名稱的聊天者對象在chatters中所對應的委托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)/ 當聊天者離開時,從字典chatters中移除包含該對象的項。/ 并從全局的多播委托上移除該對象對應的委托public void Leave()if (this.person = null)return;/獲得該聊天者對象所對應的ChatEventHandler委托ChatEventHandler chatterToRemove = getPersonHandler(this.person.Name);/執(zhí)行關鍵區(qū)域,從從字典chatters中移除包含該聊天者對象的項。lock (syncObj)chatters.Remove(this.person);/從全局的多播委托上移除該對象對應的委托ChatEvent -= chatterToRemove;ChatEventArgs e = new ChatEventArgs();e.msgType = MessageType.UserLeave;e.person = this.person;this.person = null;/將消息廣播給其他聊天者BroadcastMessage(e);/ 當chatters中的聊天者對象所對應的ChatEventHandler委托被觸發(fā)時,/ MyEventHandler方法將執(zhí)行。/ 該方法通過檢查傳遞過來的ChatEventArgs參數(shù)類型來執(zhí)行相應的客戶端/的回調接口中的方法。private void MyEventHandler(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.UserLeave:callback.UserLeave(e.person);break;catchLeave();/ 異步地觸發(fā)字典chatters中所有聊天者對象所對應的ChatEventHandler委托。/ BeginInvoke 方法可啟動異步調用。第一個參數(shù)是一個 AsyncCallback 委托,/該委托引用在異步調用完成時要調用的方法。/ 第二個參數(shù)是一個用戶定義的對象,該對象可向回調方法傳遞信息。/BeginInvoke 立即返回,不等待異步調用完成。/BeginInvoke 會返回 IAsyncResult,這個結果可用于監(jiān)視異步調用進度。private void BroadcastMessage(ChatEventArgs e)ChatEventHandler temp = ChatEvent;if (temp != null)foreach (ChatEventHandler handler in temp.GetInvocationList()handler.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);/ EndInvoke 方法檢索異步調用的結果。/調用 BeginInvoke 后可隨時調用 EndInvoke 方法;/如果異步調用尚未完成,EndInvoke 將一直阻止調用線程,/直到異步調用完成后才允許調用線程執(zhí)行。private void EndAsync(IAsyncResult ar)ChatEventHandler d = null;try/System.Runtime.Remoting.Messaging.AsyncResult封裝了異步委托上/的異步操作的結果。/AsyncResult.AsyncDelegate 屬性可用于獲取在其上調用/異步調用的委托對象。 System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messaging.AsyncResult)ar;d = (ChatEventHandler)asres.AsyncDelegate);d.EndInvoke(ar);catchChatEvent -= d;2. 宿主端的設計 設計好服務之后,宿主端的設計相對簡單,只是一個基于控制臺的應用程序。首先建立一個ServiceHost類型的對象host,然后調用ServiceHost類型的實例方法Open即可啟動宿主監(jiān)聽。需要注意的是在程序末尾應當調用Abort以及Close將資源釋放掉,或者利用using語句來自動釋放所占用的資源。下面是宿主端的實現(xiàn)代碼以及配置文件內容:static void Main(string args)Uri uri = new 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.Close(); 配置文件中,地址為net.tcp:/localhost:10001/chatservice;綁定為netTcpBinding,并且提供可靠性傳輸、不保障安全性,發(fā)送超時時間限定為1秒;契約為Chatters.IChat接口。另外設置了名稱為DuplexBinding的綁定配置,其對服務的分流作了限制,并發(fā)的Session個數(shù)的上限為10000。 3. 客戶端的設計 客戶端的設計主要包括實現(xiàn)客戶端代理與配置文件、客戶端的回調接口契約的實現(xiàn)以及客戶端登錄、主窗體、聊天窗體界面的設計與代碼邏輯實現(xiàn)這三部分。表示客戶端的各個對象之間行為關系的序列圖如圖7所示。圖7 客戶端應用的序列圖 首先是利用svcutil.exe工具自動生成的客戶端代理以及客戶端的配置文件,并做出相應的更改。打開Visual Studio 2005 命令提示,轉到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 因為用戶加入聊天這個過程是異步執(zhí)行的,所以可以利用自動生成的異步客戶端代理文件中的BeginJoin 與EndJoin 方法來替換同步的Join方法。 客戶端代理的實現(xiàn)代碼如下所示:using Common;System.CodeDom.Compiler.GeneratedCodeAttribute(System.ServiceModel, )System.ServiceModel.ServiceContractAttribute(CallbackContract = typeof(IChatCallback), SessionMode = System.ServiceModel.SessionMode.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. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 畫廊代理協(xié)議書
- 股權改制協(xié)議書
- 資產放棄協(xié)議書
- 用地變更協(xié)議書
- 花磚鋪裝協(xié)議書
- 李律師請教婚內協(xié)議書
- 股東財務協(xié)議書
- 簡約安全協(xié)議書
- 股東運營協(xié)議書
- 騰訊員工協(xié)議書
- 全國統(tǒng)一考試考務人員網(wǎng)上培訓考試試題及答案
- MOOC 隔網(wǎng)的智慧-乒羽兩項-西南交通大學 中國大學慕課答案
- 打印服務合同
- PE燃氣管道使用說明書
- 質量環(huán)境職業(yè)健康安全(QES)一體化管理手冊
- 《慧典電子病歷系統(tǒng)》操作手冊
- 配電室維保方案資料
- 下消化道出血診治PPT
- 醫(yī)院病房裝修改造工程施工方案
- 非相干散射雷達調研報告
- 設計概論 設計批評
評論
0/150
提交評論