老老實實學WCF
第九篇 消息通信模式(上) 請求應答與單向
通過前兩篇的學習,我們瞭解了服務模型的一些特性如會話和實例化,今天我們來進一步學習服務模型的另一個重要特性:消息通信模式。
WCF的服務端與客戶端在通信時有三種模式:單向模式、請求/應答模式和雙工模式。
如果選用了單向模式,調用方在向被調用方進行了調用後不期待任何迴應,被調用方在執行完調用後不給調用方任何反饋。如客戶端通過單向模式調用了一個服務端的操作後,就去幹別的了,不會等待服務端給他任何響應,他也無從得知調用是否成功,甚至連發生了錯誤也全然不知。這種模式的特點是,客戶端在調用操作後立即返回,從客戶端角度看,用戶操作的響應是非常快的,只是客戶端無法得知調用結果。
如果選用了請求/應答模式,客戶端向服務端發出調用後會一直等待服務端的回覆,服務端在執行完操作後會把結果返回給客戶端,即使服務操作簽名返回值爲void,服務端還是會返回一條空消息,告訴客戶端調用完成了,客戶端在接到返回後纔會從調用方法返回繼續進行下面的工作。這種模式的特點是客戶端總是可以知道服務執行的情況,如果出錯,錯誤也會返回,客戶端對服務的執行監控的很好,但是由於在服務返回之前客戶端會一直等待,所以如果服務端的服務執行時間比較長的話,客戶端這邊的用戶響應就會很慢,如果客戶端對服務的調用與用戶界面在同一線程,在用戶看來,應用程序就死在那裏了。
如果選用了雙工模式,客戶端和服務端都可以單獨的向對方發送消息調用,其實這種模式是在單向模式基礎上進行的,兩邊的調用都是單向調用,但是兩邊都可以獨立的進行,誰也不用等待誰,這種模式比較複雜一些,我們在下一篇再詳細的研究。
1. 如何設置消息通信模式。
雙工模式有其他的設置方式,單行模式和請求應答模式的設置位置是相同的,就是通過修改操作協定的OperationContract屬性的IsOneWay屬性來設置。如下面的代碼將HelloWCF操作協定設置爲了單向模式:
[ServiceContract] public interface IHelloWCF { [OperationContract(IsOneWay=true)] void HelloWCF(); }
如果不配置IsOneWay屬性,那麼他默認是False的,也就是說默認的消息通信模式是請求/應答模式,除非我們顯式的指定爲單向模式。
下面的代碼將HelloWCF操作協定設置爲了請求/應答模式:
[ServiceContract] public interface IHelloWCF { [OperationContract(IsOneWay=false)] void HelloWCF(); }
由於是默認值,IsOneWay屬性不配置也是可以的。
注意,在單向模式下,返回值必須是void,並且不能使用任何Out或Ref的方式返回參數值,也就是說不能以任何手段返回任何值,這是基礎結構所不允許的,這樣做會導致服務端拋出異常。而在請求/應答模式下,這些都是可以的,即使沒有返回值(返回值爲void),返回消息也會照樣發送,只不過是個空消息。
2. 兩種模式的例子
首先我們看一個請求/應答模式的例子,我用的還是前幾篇中使用的IIS宿主服務的例子,如果你忘了,翻回去熟悉一下。
我們讓服務端的HelloWCF在返回"Hello WCF!"字符串之前,先磨蹭一會,讓他在線程上休眠一會兒。
HelloWCFService.CS的源代碼如下:
using System; using System.ServiceModel; namespace LearnWCF { [ServiceContract] public interface IHelloWCF { [OperationContract(IsOneWay=false)] string HelloWCF(); } public class HelloWCFService : IHelloWCF { private int _Counter; public string HelloWCF() { System.Threading.Thread.Sleep(3000); return "Hello WCF!"; } } }
沒什麼變化,就是讓他在線程上Sleep 3秒。
下面是Web.Config文件,也沒什麼變化:
<configuration> <system.serviceModel> <services> <service name="LearnWCF.HelloWCFService" behaviorConfiguration="metadataExchange"> <endpoint address="" binding="wsHttpBinding" contract="LearnWCF.IHelloWCF"/> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name="metadataExchange"> <serviceMetadata httpGetEnabled="true" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
下面是SVC文件,就一行代碼,指示了這是個WCF服務,並指定了後臺類型:
<%@ServiceHost language=c# Debug="true" Service="LearnWCF.HelloWCFService"%>
把SVC文件和Web.Config文件放在網站根文件夾下,CS文件放在App_Code文件夾下,啓動IIS,服務就寄宿好了,如果你忘記了如何在IIS中寄宿,馬上翻回第三篇熟悉一下。
用SVCUTIL.EXE或添加服務引用來生成客戶端,爲了能看出調用的時間,我們在調用前和調用後分別把時間輸出來。Program.cs代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace ConsoleClient { class Program { static void Main(string[] args) { Services.HelloWCFClient client = new Services.HelloWCFClient(); Console.WriteLine(DateTime.Now.ToLongTimeString()); Console.WriteLine(client.HelloWCF()); Console.WriteLine(DateTime.Now.ToLongTimeString()); Console.ReadLine(); } } }
F5運行一下,結果如下:
可以看到,整個調用花費了4秒鐘,除了服務方法中Sleep了3秒,建立會話通訊什麼的還用了1秒,在服務端方法Sleep的時候,客戶端一直在等待。
接下來,我們再看單向模式的情況,我們修改一下服務協定的代碼,讓其採用單向模式,但是注意,此時不能有返回值了,必須設爲void,服務方法中就是睡3秒,其他的什麼也不做。
using System; using System.ServiceModel; namespace LearnWCF { [ServiceContract] public interface IHelloWCF { [OperationContract(IsOneWay=true)] void HelloWCF(); } public class HelloWCFService : IHelloWCF { private int _Counter; public void HelloWCF() { System.Threading.Thread.Sleep(3000); } } }
客戶端需要重新下載一下元數據或更新一下服務引用,因爲服務協定的內容變了,客戶端Program.CS代碼如下:using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace ConsoleClient { class Program { static void Main(string[] args) { Services.HelloWCFClient client = new Services.HelloWCFClient(); Console.WriteLine(DateTime.Now.ToLongTimeString()); client.HelloWCF(); Console.WriteLine(DateTime.Now.ToLongTimeString()); Console.ReadLine(); } } }
F5看看結果:可以看到只用了1秒,客戶端與服務端建立會話後把調用送出就立即返回了,沒有等待服務端睡那三秒,當然此時的客戶端也根本就不知道服務端在做什麼。
注意,請求應答模式是需要會話支持的,必須使用支持會話的綁定,而且服務協定的SessionMode必須至少爲Allowed,服務類的ServiceBehavior的InstanceContextMode必須是PerSession,我們在這裏沒有配置,因爲他們是默認的,但是我們必須知道他們需要這樣的配置才能支持請求/應答模式。
如果你在試驗中遇到了莫名其妙的問題,嘗試把客戶端服務引用全部刪掉重新添加服務引用,因爲有的時候更新服務引用不總是那麼好用。
3. 總結
通過這一篇的學習,我們瞭解了消息通訊的兩種基本模式,在這個基礎上還有更加複雜的雙工通訊模式,我們在下一篇中詳細研究。