【转】WCF请求应答(Request-Reply)、单向操作(One-Way)、回调操作(Call Back)
【1】請求應答(Request-Reply):
?????? 請求應答模式是默認的操作模式。這與經典的C/S編程類似,客戶端發送請求,阻塞客戶端進程,服務端返回操作結果。請求應答模式與綁定對應關系 :
除了NetPeerTcpBinding和NetMsmqBinding綁定,所有的綁定均支持請求-應答操作。
【2】單向操作(One-Way):
【2.1】概念:
??? 簡單來說,單向操作沒有返回值,客戶端只管調用,不管結果。單向操作客戶端一旦發出請求,WCF會生成一個請求,不會給客戶端返回任何消息。單向操作不同 于異步操作,雖然單向操作只是在發出調用的瞬間阻塞客戶端,但如果發出多個單向調用,WCF會將請求調用放入隊列,并在某個時候執行。隊列存儲調用的個數 是有限的,一旦發出的調用個數超出了隊列存儲調用的設置值,則會發生阻塞現象,因為調用無法放入隊列。當隊列的請求出列后,產生阻塞的調用就會放入隊列, 并解除對客戶端的阻塞。綁定協議與單向請求模式關系:
- 綁定協議名稱????????????支持可靠性?? ????? 默認可靠性???? ????? 支持有序傳遞?????? 單向模式
- BasicHttpBinding????????????? ??No?????????????? N/A?????????????????? ?No??????????????????Yes
- NetTcpBinding?? ??????????????? Yes???????????????Off??????????????????? Yes??????????????????Yes
- NetPeerTcpBinding??????????????No? ????????????? N/A?????????????????? No???????????????????Yes
- NetNamedPipeBinding???????? ?No? ????????????? N/A (On)?????????? Yes??????????????????Yes
- WSHttpBinding? ??????????? ?? ?Yes???????????????Off??????????????????? Yes????????????????? Yes
- WSFederationHttpBinding?? Yes???????????????Off??????????????????? ?Yes????????????????? Yes
- WSDualHttpBinding??? ?????? ?Yes ????????????? On???????????????????? Yes??????????????????Yes
- NetMsmqBinding??????? ?????? ? No? ????????????? N/A????????????????????No???????????????????Yes
- MsmqIntegrationBinding???????No???????????????N/A??????????????????? No?????????????????? Yes
和請求應答模式不同。所有的WCF綁定通信協議都支持單向操作。? 【2.2】實現方式: ??? 配置單向操作的方式也很簡單,WCF的OperationContract 定義了IsOneWay屬性。我們設置設置單向操作的方法是利用OperationContract特性的IsOneWay屬性,例如:
//操作契約,單調操作,不返回應答消息,會話服務中,保證是最后一個操作 ????????[OperationContract(IsOneWay=true,IsInitiating=false,IsTerminating=true)]// ????????void?SayHello2(string?name);??? 單向操作配置的屬性定義在操作契約級別上。而不是用在服務契約級別。
【2.3】單向操作小節:???
?(1)被設置為單向操作的方法不能包含返回值,即它的返回值只能為void,否則會拋出InvalidOperationException異常。? ?(2)在會話契約中雖然允許定義單向操作([ServiceContract( SessionMode =SessionMode.Required, Namespace = "http://www.cnblogs.com/frank_xl/")]),但由于單向操作服務端管理客戶端會話狀態十分困難,因而,單向操作的最佳 適用場景是在單調服務或單例服務中。如果在會話契約中定義了單向操作,就必須保證單向操作是終止會話的最后一個操作,返回void類型值。這可以通過分步 操作來實現。代碼如下:
//1.單向服務契約,會話服務 ????[ServiceContract(?SessionMode?=SessionMode.Required,?Namespace?=?"http://www.cnblogs.com/frank_xl/")] ????public?interface?IWCFServiceOneWay ????{ ????????//操作契約,單調操作,不返回應答消息,會話服務中,保證是最后一個操作 ????????[OperationContract(IsOneWay=true,IsInitiating=false,IsTerminating=true)]// ????????void?SayHello2(string?name); ????????//操作契約, ????????[OperationContract] ????????string?SayHello1(string?name);
????}?(3)如果因為通信(地址宿主)問題,調用操作失敗,單向操作如果拋出異常;客戶端受服務端異常影響,取決于實例模式以及使用綁定。
【3】回調操作(Call Back):
【3.1】概念:
??? 回調不是一個新的概念,早在C語言里就有過,C#里更是有委托實現回調機制。軟件模塊之間總是存在著一定的接口,從調用方式上,可以把他們分為三類:同步 調用、回調和異步調用。同步調用是一種阻塞式調用,調用方要等待對方執行完畢才返回,它是一種單向調用;回調是一種雙向調用模式,也就是說,被調用方在接 口被調用時也會調用對方的接口;異步調用是一種類似消息或事件的機制,不過它的調用方向剛好相反,接口的服務在收到某種訊息或發生某種事件時,會主動通知 客戶方(即調用客戶方的接口)。回調和異步調用的關系非常緊密,通常我們使用回調來實現異步消息的注冊,通過異步調用來實現消息的通知。同步調用是三者當 中最簡單的,而回調又常常是異步調用的基礎,因此,下面我們著重討論回調機制在WCF軟件架構中的實現。回調機制如圖所示:?
????并非所有的綁定協議都支持回調,http本質上是無連接的協議,TCP/IP協議才會在客戶端和服務端維持通信信道。兩者之間的對應關系如下:
- 綁定協議名稱????????????支持可靠性?? ????? 默認可靠性???? ????? 支持有序傳遞?????? 回調模式
- BasicHttpBinding????????????? ??No?????????????? N/A?????????????????? ?No??????????????????No
- NetTcpBinding?? ??????????????? Yes???????????????Off??????????????????? Yes??????????????????Yes
- NetPeerTcpBinding??????????????No? ????????????? N/A?????????????????? No???????????????????No
- NetNamedPipeBinding???????? ?No? ????????????? N/A (On)?????????? Yes??????????????????Yes
- WSHttpBinding? ??????????? ?? ?Yes???????????????Off??????????????????? Yes??????????????????No
- WSFederationHttpBinding?? Yes???????????????Off??????????????????? ?Yes??????????????????No
- WSDualHttpBinding??? ?????? ?Yes ????????????? On???????????????????? Yes??????????????????Yes
- NetMsmqBinding??????? ?????? ? No? ????????????? N/A????????????????????No???????????????????No
- MsmqIntegrationBinding???????No???????????????N/A??????????????????? No?????????????????? No
??? BasicHttpBinding,WSHttpBinding綁定協議不支持回調操作。NetTcpBinding和 NetNamedPipeBinding綁定支持回調操作;具有可靠消息傳輸的WSDualHttpBinding綁定是通過設置兩個HTTP信道來支持 雙向通信。??
-
【3.2】實現代碼: ?? 一個服務契約只能包含一個回調契約。通過ServiceContract特性,可以指定回調契約:
//0.回調服務契約,由于回調方法在客戶端執行,因此無須添加?ServiceContractAttribute。對于回調操作,服務器無須獲取其返回信息,因此添加?IsOneWay=true?特性參數。 ????public?interface?IWCFServiceCallBack ????{ ????????//操作契約 ????????[OperationContract(IsOneWay=true)]// ????????void?SayHelloCalllBack(); ????} ????//1.服務契約,指定?SessionMode?和回調類型。 ????[ServiceContract(SessionMode?=?SessionMode.Required,CallbackContract?=?typeof(IWCFServiceCallBack))] ????public?interface?IWCFService ????{ ????????//操作契約, ????????[OperationContract] ????????string?SayHelloToUser(string?name);
????}??? 回調契約無須標記ServiceContract特性,但是在回調契約中必須為服務的操作標記OperationContract特性。? 在導入回調契約的元數據中,回調契約以Callback結尾。服務端反序列化本地代碼的時候會生成客戶端回調操作契約Callback后綴。
【3.3】回調小節: (1)如果使用了回調契約,回調契約不需要ServiceContract特性,設置為回調契約就默認了服務契約的特性。 (2)客戶端通過回調傳遞給服務端的消息包含了回調契約終結點的引用。在服務端,可以通過OperationContext類的泛型方法GetCallbackChannel<T>()獲得。代碼如下:?
//獲取客戶端通道實例 ????????IWCFServiceCallBack?callback?=?OperationContext.Current.GetCallbackChannel<IWCFServiceCallBack>();【4】示例代碼分析:
??? 直接看概念還不能很好的理解回調的機制,下面我們來具體看看WCF里如何實現回調。客戶端調用服務操作,服務操作通過客戶端上下文實例調用客戶端操作,這 是回調操作的基本過程。一下是具體的代碼實現講解過程。這里只介紹回調操作的具體實現代碼。單向操作過于簡單,注釋也比較詳細,大家可以參考上傳的代碼。
【4.1】服務端:
???? 定義一個回調契約IWCFServiceCallBack,服務契約IWCFService、服務類WCFService : IWCFService繼承服務契約。代碼如下:
//1.回調服務契約,由于回調方法在客戶端執行,因此無須添加?ServiceContractAttribute。對于回調操作,服務器無須獲取其返回信息,因此添加?IsOneWay=true?特性參數。 ????public?interface?IWCFServiceCallBack ????{ ????????//操作契約 ????????[OperationContract()]// ????????void?SayHelloCalllBack(); ????} ????//2.服務契約,指定?CallbackContract?回調契約。 ????[ServiceContract(CallbackContract?=?typeof(IWCFServiceCallBack))] ????public?interface?IWCFService ????{ ????????//操作契約, ????????[OperationContract] ????????string?SayHelloToUser(string?name);
????} ????//3.服務類,繼承接口。實現服務契約定義的操作 ????public?class?WCFService?:?IWCFService ????{ ????????//獲取當前操作客戶端對象實例 ????????IWCFServiceCallBack?callback?=?OperationContext.Current.GetCallbackChannel<IWCFServiceCallBack>();
????????//實現接口定義的方法 ????????public?string?SayHelloToUser(string?name) ????????{ ????????????//Action<IWCFServiceCallBack>?invoke?=?delegate(IWCFServiceCallBack?callBack) ????????????//{?callBack.SayHelloCalllBack();?}; ????????????callback(invoke); ????????????Console.WriteLine("Hello!?{0}?,?",?name); ????????????callback.SayHelloCalllBack(); ????????????return?"Hello!?"?+?name; ????????} ????}?? 服務端?獲取當前操作客戶端對象實例 ??????? IWCFServiceCallBack callback = OperationContext.Current.GetCallbackChannel<IWCFServiceCallBack> ();callback.SayHelloCalllBack();執行回調客戶端當前實例方法。
【4.2】宿主:
???? 宿主啟動和綁定節點配置和前面幾節講解的配置過程類似。這里配置的協議是TCP。配置文件代碼如下:
<service???behaviorConfiguration="WCFService.WCFServiceBehavior"?name="WCFService.WCFService"> ????????<endpoint ??????????address="net.tcp://localhost:9004/WCFService" ??????????binding="netTcpBinding" ??????????contract="WCFService.IWCFService"> ????????</endpoint> ????????<endpoint?address="mex"?binding="mexHttpBinding"?contract="IMetadataExchange"?/> ????????<host> ??????????<baseAddresses> ????????????<add?baseAddress="http://localhost:9003/"/> ????????????<add?baseAddress="net.tcp://localhost:9004/"/> ??????????</baseAddresses> ????????</host> ??????</service>?
【4.3】客戶端:
??? 運行服務托管宿主,客戶端添加服務引用,反序列化服務元數據,如圖:
??? 修改客戶端代碼,重新實現回調契約的操作方法,如下:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",?"3.0.0.0")] ????public?class?WCFServiceCallback?:?IWCFServiceCallback ????{
????????public?void?SayHelloCalllBack() ????????{ ????????????Console.WriteLine("Client?method?is?CallBacking"); ????????}?? ????}? 測試回調代碼,我們實例化一個回調類的實例,然后作為上下文實例的參數。最后把上下文作為參數實例化一個客戶端代理。具體代碼如下:
//CallBack?回調服務 ????????????Console.WriteLine("Call?Back?Operation?Test"); ????????????WCFClientCallBack.IWCFServiceCallback?callBack?=?new?WCFClientCallBack.WCFServiceCallback();
????????????InstanceContext?context?=?new?InstanceContext(callBack); ????????????WCFClientCallBack.WCFServiceClient?WCFServiceCallBackClientProxy?=?new?WCFClientCallBack.WCFServiceClient(context,?"NetTcpBinding_IWCFService"); ????????????//通過代理調用調用SayHelloToUser,傳遞對象 ????????????Console.WriteLine(WCFServiceCallBackClientProxy.SayHelloToUser("Frank?Xu?Lei?Call?Back"));?
【4.4】運行結果:
??? 這里的運行結果包括單向操作和回調操作結果,客戶端調用一個服務操作,服務操作再通過客戶端上下文實例引用調用客戶端操作。成功執行回調操作。結果如圖:
【5】總結:
(1)?服務對回調的調用可能會產生死鎖。就是指當回調的應答消息也需要獲得與服務實例關聯的相同的鎖時,會導致死鎖。此時服務線程已經被阻塞,服 務操作正在等待回調操作執行完畢,而回調操作卻又在等待服務釋放鎖。 解決死鎖的辦法:<1>將服務配置為允許多線程訪問,會增加服務開發者負擔。
<2>將回調設置為重入(Reentrancy)??? //3.服務類,繼承接口。實現服務契約定義的操作 ??? [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)] ??? public class WCFService : IWCFService ??? {...}??
-
? 。所謂“重入”,是指對同步域擁有獨占訪問權的線程A調用了同步域之外對象的方法,此時,另外的線程B若要訪問該同步域,則線程A將釋放對同步域的鎖,允 許線程B進入。直到線程B執行完畢并釋放對同步域的鎖后,線程A將重新進入該同步域。由于服務被配置為重入,則服務調用回調引用時會釋放鎖。然后將回調返 回給客戶端,控制權則返回給服務,服務會重入并重新獲取鎖。? <3>將回調操作設置為單向操作(??????? [OperationContract(IsOneWay=true)]//?void SayHelloCalllBack();)。此時,回調調用不會產生應答消息,服務操作一旦執行了回調操作,就會繼續執行,回調對象不會爭用與服務實例 關聯的鎖,從而解決了死鎖問題。??
?(2)單調服務的回調問題需要考慮回調對象的引用存儲問題,因為每次調用結束都會釋放服務實例對象,客戶端的狀態也會丟失。
(3)單例服務的問題是,猶豫所有的服務共享一個實例,而其生命周期的問題,回調的引用計數會增加。
(4)回調契約的層級問題,一旦一個服務契約提供了回調契約的定義,其所有的子接口必須生命和其一致的回調契約。
(5)NetTcpBinding和NetNamedPipeBinding綁定支持回調操作,并且和客戶端共享一個通道端口;具有可靠消息傳輸的WSHttpBinding需要維護連個端口有可能產生端口沖突,編程時值得注意。
??? 以上就是本節的全部內容,下一節我們會介紹WCF流操作的一些內容,這里也值得學習。因為在前面的WSE3.0文章里我介紹了WSE優化文件傳輸的問題。 當時也提到了流操作的概念。WCF框架也提供了流操作的支持,同樣值得我們學習。這里對大規模數據對象的操作和處理有重要作用,下一個準備介紹一下。
轉載于:https://www.cnblogs.com/yf2011/articles/5166119.html
總結
以上是生活随笔為你收集整理的【转】WCF请求应答(Request-Reply)、单向操作(One-Way)、回调操作(Call Back)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于二分类的评价指标体系
- 下一篇: 沈园(两首)