WCF分布式開發步步為贏(10):請求應答(Request-Reply)、單向操作(One-Way)、回調操作(Call Back).
WCF除了支持經典的請求應答(Request-Reply)模式外,還提供了什么操作調用模式,他們有什么不同以及我們如何在開發中使用這些操作調用模式。今天本節文章里會詳細介紹。WCF分布式開發步步為贏(10):請求應答(Request-Reply)、單向操作(One-Way)、回調操作(Call Back).本文結構:【1】請求應答(Request-Reply)、【2】單向操作(One-Way)、【3】回調操作(Call Back)、【4】示例代碼分析、【5】總結。最后上傳本文的示例代碼。
WCF除了支持經典的請求/應答模式意外,還提供了對單向操作、雙向回調操作模式的支持,此外還有流操作(后者與WSE3.0提供的優化傳輸機制類似,我曾經在這個文章里進行過講解WSE3.0構建Web服務安全(4):MTOM消息傳輸優化和文件上傳、下載 )。今天我們會介紹幾種操作調用模式的概念,區別,實現機制,以及如何在代碼中實現他們,最后給出的要注意的細節問題。
【1】請求應答(Request-Reply):
請求應答模式是默認的操作模式。這與經典的C/S編程類似,客戶端發送請求,阻塞客戶端進程,服務端返回操作結果。請求應答模式與綁定對應關系 :
- 綁定協議名稱 支持可靠性 默認可靠性 支持有序傳遞 請求應答模式
- BasicHttpBinding No N/A No Yes
- NetTcpBinding Yes Off Yes Yes
- NetPeerTcpBinding No N/A No No
- 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 No
- MsmqIntegrationBinding No N/A No Yes
除了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.rzrgm.cn/frank_xl/")]),但由于單向操作服務端管理客戶端會話狀態十分困難,因而,單向操作的最佳適用場景是在單調服務或單例服務中。如果在會話契約中定義了單向操作,就必須保證單向操作是終止會話的最后一個操作,返回void類型值。這可以通過分步操作來實現。代碼如下://1.單向服務契約,會話服務
[ServiceContract( SessionMode =SessionMode.Required, Namespace = "http://www.rzrgm.cn/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框架也提供了流操作的支持,同樣值得我們學習。這里對大規模數據對象的操作和處理有重要作用,下一個準備介紹一下。最后上傳本文的示例代碼供大家參考/Files/frank_xl/WCFServiceOperationFrankXuLei20090503.rar。
-
參考文章:
1.Programming WCF Services
2.WSE3.0構建Web服務安全(4):MTOM消息傳輸優化和文件上傳、下載
3.什么叫回調函數,http://hi.baidu.com/zhuyipeng/blog/item/863fefdb7c736c63d1164eec.htm


浙公網安備 33010602011771號