WCF入門四[WCF的通訊模式]
一、概述
WCF的通訊模式有三種:請求/響應模式、單向模式和雙工通訊。
二、請求/響應模式
請求/響應模式就是WCF的預設模式,前面幾篇隨筆中的示例都是這種模式,當客戶端傳送請求後(非非同步狀態下),即使返回的是void客戶端會一直等待服務端的響應後才繼續下面的操作。
優點:可以及時的向客戶端返回錯誤資訊。
缺點:面對服務端需要長時間處理的情況下,降低客戶端的響應速度和效能。
這個大家都好理解,就不再複述。
三、單向模式
單向模式和請求/響應模式相反,單向模式就是客戶端傳送請求後直接進行接下來的操作,不會等待服務端的響應,並且服務端也不會發送響應。所以單向模式的方法不得宣告輸出引數、返回值和引用引數。
單向模式的需要在OpertaionContract中設定IsOneWay=true ,如下所示:
[OperationContract(IsOneWay = true)] void ShowDay(string day);
優點:客戶端反應速度快、效能強,當然排除需要超大量傳輸引數的情況。
缺點:不能及時獲取服務端是否操作結果。
四、雙工通訊
雙工通訊就是實現客戶端和服務端方法可以相互呼叫。一般情況下都是客戶端呼叫服務端的方法,但是雙工通訊可以讓服務端呼叫客戶端的方法。雙工通訊時建立在上述兩種方式之上的,並不是相互衝突的。
1.服務端
定義服務契約和回撥契約,服務契約必須通過特性[ServiceContract(CallbackContract = typeof(ICustomCallback))]來關聯回撥契約,回撥契約不必宣告ServiceContract特性,但是方法必須宣告OperationContract否則在客戶端引用服務後無法使用該方法,這裡我們用單向模式。
namespace WcfServiceLibrary { // 注意: 使用“重構”選單上的“重新命名”命令,可以同時更改程式碼和配置檔案中的介面名“IDay”。 [ServiceContract(CallbackContract = typeof(ICustomCallback))] public interface IDay { [OperationContract] string ShowDay(int day); } public interface ICustomCallback { [OperationContract(IsOneWay=true)] void UserCustomMethod(string str); } }
在配置檔案中binding指定WSDualHttpBinding,支援回撥的繫結有4種:WSDualHttpBinding、NetTcpBinding、NetNamedPipeBinding、NetPeerTcpBinding,我這裡用第一種。
<endpoint address="" binding="wsDualHttpBinding" contract="WcfServiceLibrary.IDay">
實現服務契約介面,不需要實現回撥契約的介面,因為回撥是使用客戶端的方法所以在客戶端實現。OperationContext是WCF中非常重要的物件,它表示操作執行的上下文,通過OperationContext的方法GetCallbackChannel<T>()來獲取回撥物件後使用回撥方法。
namespace WcfServiceLibrary { // 注意: 使用“重構”選單上的“重新命名”命令,可以同時更改程式碼和配置檔案中的類名“Day”。 public class Day : IDay { ICustomCallback customCallback = null; public Day() { customCallback = OperationContext.Current.GetCallbackChannel<ICustomCallback>(); } public string ShowDay(int day) { customCallback.UserCustomMethod("哈哈哈哈");//使用回撥方法 return string.Format("WCF服務返回Day是:{0}", day); } } }
2.客戶端
客戶端引用WCF服務後會在Service References檔案加下生成一個服務類,裡面定義了回撥介面IDayCallback如下:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] public interface IDayCallback { [System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://tempuri.org/IDay/UserCustomMethod")] void UserCustomMethod(string str); }
實現回撥方法,並呼叫WCF服務:
class Program { static void Main(string[] args) { InstanceContext instanceContext = new InstanceContext(new CustomCallback()); DayClient client = new DayClient(instanceContext); Console.WriteLine(client.ShowDay(666)); Console.ReadLine(); } } public class CustomCallback : IDayCallback { public void UserCustomMethod(string str) { Console.WriteLine("這是客戶端方法:{0}",str); } }
除錯可以看到,在服務端成功呼叫了客戶端的方法UserCustomMethod(string str):
五、回撥死鎖
由於雙工通訊下客戶端和服務端可以互相呼叫對方的方法,所以會出現雙方都等待對方響應的情況,而造成死鎖而報錯。上面的例子沒有造成死鎖是因為使用了單向模式,不需要等待響應。那麼如果需要使用帶有返回值的回撥方法那該怎麼辦呢?
我們將上述服務端回撥契約定義的回撥方法改為string UserCustomMethod(string str)並去掉IsOneWay = true,然後在服務端進行以下改造:
// 注意: 使用“重構”選單上的“重新命名”命令,可以同時更改程式碼和配置檔案中的類名“Day”。 public class Day : IDay { ICustomCallback customCallback = null; public Day() { customCallback = OperationContext.Current.GetCallbackChannel<ICustomCallback>(); } public string ShowDay(int day) { string str = customCallback.UserCustomMethod("哈哈哈哈"); return string.Format("WCF服務返回Day是:{0}。{1}", day, str); } }
之後在客戶端更新服務,執行報以下錯誤:
很明顯服務不支援併發,由於等待響應而產生了死鎖。可以修改ServiceBehavior特性中的ConcurrencyMode就可以了。那麼我們修改下程式碼如下,ServiceBehavior只能宣告類:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] public class Day : IDay { ICustomCallback customCallback = null; public Day() { customCallback = OperationContext.Current.GetCallbackChannel<ICustomCallback>(); } public string ShowDay(int day) { string str = customCallback.UserCustomMethod("哈哈哈哈"); return string.Format("WCF服務返回Day是:{0}。{1}", day, str); } }
再次執行客戶端就成功啦:
六、說明
這個隨筆是我自己學習流程的一個記錄,和大家共勉。