1. 程式人生 > >WCF入門四[WCF的通訊模式]

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);
        }
    }

  再次執行客戶端就成功啦:

六、說明

這個隨筆是我自己學習流程的一個記錄,和大家共勉。