您好,登錄后才能下訂單哦!
第六篇:單向與雙向通訊
項目開發中我們時常會遇到需要異步調用的問題,有時忽略服務端的返回值,有時希望服務端在需要的時候回調,今天就來看看在WCF中如何實現。
先看不需要服務端返回值的單向調用,老規矩,直接上代碼,再解釋。
1、服務端
契約接口中增加一個Sleep方法:
- using System;
- using System.ServiceModel;
- using System.Text;
- namespace Server
- {
- [ServiceContract(Namespace="WCF.Demo")]
- public interface IData
- {
- [OperationContract]
- string SayHello(string userName);
- /// <summary>
- /// IsOneWay = true 表明這是一個單向調用,注意返回值是void,因為既然是單向調用,客戶端肯定不會等待接收返回值的
- /// </summary>
- [OperationContract(IsOneWay = true)]
- void Sleep();
- }
- }
對應的實現類中,我們來實現這個方法:
- using System;
- using System.Text;
- namespace Server
- {
- public class DataProvider : IData
- {
- public string SayHello(string userName)
- {
- return string.Format("Hello {0}.", userName);
- }
- /// <summary>
- /// 實現Sleep方法,暫時不做任何事情,只是睡眠5秒
- /// </summary>
- public void Sleep()
- {
- Thread.Sleep(5000);
- }
- }
- }
App.config就不再列出來了,里面用的是一個netTcpBinding的endpoint。
2、客戶端
首先別忘了客戶端的契約要與服務端保持一致,App.config也不列出來了,里面有對應的endpoint。主要是調用的代碼:
- using System;
- using System.ServiceModel;
- using System.ServiceModel.Channels;
- namespace Client
- {
- class Program
- {
- static void Main(string[] args)
- {
- var proxy = new ChannelFactory<Server.IData>("DataService").CreateChannel();
- //先調用SayHello方法
- Console.WriteLine(proxy.SayHello("WCF"));
- //調用一下Sleep方法,按我們的設想,它應該是異步的,所以不會阻塞后面的調用
- proxy.Sleep();
- //再調用一次SayHello方法
- Console.WriteLine(proxy.SayHello("WCF"));
- //關閉連接
- ((IChannel)proxy).Close();
- }
- }
按我們的設想,兩次SayHello調用之間應該沒有延遲,因為Sleep是異步的嘛,編譯運行一下,結果…… 中間卡住了5秒,這是為什么呢?
這其中涉及到一個并發模型的問題,默認情況下,WCF以單線程模型對外提供服務,也就是說,只能一個一個處理請求,即使是一個OneWay的單向調用,也只能等它處理完后才會接著處理后面的SayHello請求,所以會卡5秒。
并發模式有以下三種,MSDN上的介紹有點復雜,我給簡化一下:
Single:單線程調用,請求只能一個一個處理; Reentrant:可重入的單線程調用,本質仍是單線程,處理回調時,回調請求會進入隊列尾部排隊; Multiple:多線程調用,請求是并發的響應的; |
調置服務并發模型是在契約的實現類上,我們為DataService類加一個Attribute:
- /// <summary>
- /// 用ServiceBehavior為契約實現類標定行為屬性,此處指定并發模型為ConcurrencyMode.Multiple,即并發訪問
- /// </summary>
- [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
- public class DataProvider : IData
- {
- //略
- }
這回再編譯運行一下,連續打出了2行 Hello WCF,中間不再阻塞了。
現在我們再來看看雙向通訊的問題。雙向通訊可以基于HTTP、TCP、Named Pipe、MSMQ,但要注意,basicHttpBinding和wsHttpBinding不行,要換用wsDualHttpBinding,它會創建兩個連接來進行雙向通訊。至于TCP,它天然就是雙向通訊的。
1、服務端
服務契約要進行修改,增加關于回調的契約:
- using System;
- using System.ServiceModel;
- using System.ServiceModel.Description;
- namespace Server
- {
- /// <summary>
- /// 增加了CallbackContract的標識,用于指明針對此服務契約的回調契約是IDataCallback
- /// </summary>
- [ServiceContract(Namespace = "WCF.Demo", CallbackContract = typeof(IDataCallback))]
- public interface IData
- {
- [OperationContract]
- string SayHello(string userName);
- [OperationContract(IsOneWay = true)]
- void Sleep();
- }
- /// <summary>
- /// 定義服務回調契約,注意它沒有契約標識,只是個一般接口
- /// </summary>
- public interface IDataCallback
- {
- /// <summary>
- /// 定義一個回調方法,由于回調不可能要求對方再響應,所以也標識成OneWay的調用,同樣不需要有返回值
- /// </summary>
- [OperationContract(IsOneWay = true)]
- void SleepCallback(string text);
- }
- }
對應的契約實現類要修改一下:
- using System;
- using System.ServiceModel;
- using System.ServiceModel.Description;
- using System.Threading;
- using System.Net;
- namespace Server
- {
- [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
- public class DataProvider : IData
- {
- public string SayHello(string userName)
- {
- string.Format("Hello {0}.", userName);
- }
- public void Sleep()
- {
- //先睡5秒
- Thread.Sleep(5000);
- //用OperationContext.Current來獲取指定類型的回調對象
- var callback = OperationContext.Current.GetCallbackChannel<IDataCallback>();
- //回調SleepCallback方法,并傳遞參數
- callback.SleepCallback("睡醒了");
- }
- }
- }
2、客戶端
仍然提醒一下別忘了把新的服務契約更新到客戶端。客戶端的調用要調整一下:
- using System;
- using System.ServiceModel;
- using System.ServiceModel.Channels;
- namespace Client
- {
- class Program
- {
- static void Main(string[] args)
- {
- //定義一個實現回調接口的類實例
- var context = new DataCallbackImp();
- //創建代理的時候變了,要用DuplexChannelFactory,因為IData契約已經標識了有回調,所以必須要用支持雙向通訊的ChannelFactory,傳入剛才創建的回調實例
- var proxy = new DuplexChannelFactory<Server.IData>(context, "DataService").CreateChannel();
- //調用Sleep
- proxy.Sleep();
- //調用SayHello方法
- Console.WriteLine(proxy.SayHello("WCF"));
- //等待按任意鍵,先不要關連接
- Console.ReadKey();
- ((IChannel)proxy).Close();
- }
- /// <summary>
- /// 實現回調接口中的類,圖省事寫到這里了
- /// </summary>
- class DataCallbackImp : Server.IDataCallback
- {
- /// <summary>
- /// 實現SleepCallback方法
- /// </summary>
- public void SleepCallback(string text)
- {
- Console.WriteLine("收到回調了:" + text);
- }
- }
- }
編譯運行,屏幕先顯示一行“Hello WCF.”,過5秒后顯示“收到回調了:睡醒了”。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。