十年網(wǎng)站開發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營(yíng)維護(hù)+專業(yè)推廣+無(wú)憂售后,網(wǎng)站問題一站解決
第六篇:?jiǎn)蜗蚺c雙向通訊
創(chuàng)新互聯(lián)公司10多年成都定制網(wǎng)頁(yè)設(shè)計(jì)服務(wù);為您提供網(wǎng)站建設(shè),網(wǎng)站制作,網(wǎng)頁(yè)設(shè)計(jì)及高端網(wǎng)站定制服務(wù),成都定制網(wǎng)頁(yè)設(shè)計(jì)及推廣,對(duì)成都塔吊租賃等多個(gè)方面擁有多年的網(wǎng)站設(shè)計(jì)經(jīng)驗(yàn)的網(wǎng)站建設(shè)公司。
項(xiàng)目開發(fā)中我們時(shí)常會(huì)遇到需要異步調(diào)用的問題,有時(shí)忽略服務(wù)端的返回值,有時(shí)希望服務(wù)端在需要的時(shí)候回調(diào),今天就來(lái)看看在WCF中如何實(shí)現(xiàn)。
先看不需要服務(wù)端返回值的單向調(diào)用,老規(guī)矩,直接上代碼,再解釋。
1、服務(wù)端
契約接口中增加一個(gè)Sleep方法:
- using System;
- using System.ServiceModel;
- using System.Text;
- namespace Server
- {
- [ServiceContract(Namespace="WCF.Demo")]
- public interface IData
- {
- [OperationContract]
- string SayHello(string userName);
- ///
- /// IsOneWay = true 表明這是一個(gè)單向調(diào)用,注意返回值是void,因?yàn)榧热皇菃蜗蛘{(diào)用,客戶端肯定不會(huì)等待接收返回值的
- ///
- [OperationContract(IsOneWay = true)]
- void Sleep();
- }
- }
對(duì)應(yīng)的實(shí)現(xiàn)類中,我們來(lái)實(shí)現(xiàn)這個(gè)方法:
- using System;
- using System.Text;
- namespace Server
- {
- public class DataProvider : IData
- {
- public string SayHello(string userName)
- {
- return string.Format("Hello {0}.", userName);
- }
- ///
- /// 實(shí)現(xiàn)Sleep方法,暫時(shí)不做任何事情,只是睡眠5秒
- ///
- public void Sleep()
- {
- Thread.Sleep(5000);
- }
- }
- }
App.config就不再列出來(lái)了,里面用的是一個(gè)netTcpBinding的endpoint。
2、客戶端
首先別忘了客戶端的契約要與服務(wù)端保持一致,App.config也不列出來(lái)了,里面有對(duì)應(yīng)的endpoint。主要是調(diào)用的代碼:
- using System;
- using System.ServiceModel;
- using System.ServiceModel.Channels;
- namespace Client
- {
- class Program
- {
- static void Main(string[] args)
- {
- var proxy = new ChannelFactory
("DataService").CreateChannel(); - //先調(diào)用SayHello方法
- Console.WriteLine(proxy.SayHello("WCF"));
- //調(diào)用一下Sleep方法,按我們的設(shè)想,它應(yīng)該是異步的,所以不會(huì)阻塞后面的調(diào)用
- proxy.Sleep();
- //再調(diào)用一次SayHello方法
- Console.WriteLine(proxy.SayHello("WCF"));
- //關(guān)閉連接
- ((IChannel)proxy).Close();
- }
- }
按我們的設(shè)想,兩次SayHello調(diào)用之間應(yīng)該沒有延遲,因?yàn)镾leep是異步的嘛,編譯運(yùn)行一下,結(jié)果…… 中間卡住了5秒,這是為什么呢?
這其中涉及到一個(gè)并發(fā)模型的問題,默認(rèn)情況下,WCF以單線程模型對(duì)外提供服務(wù),也就是說,只能一個(gè)一個(gè)處理請(qǐng)求,即使是一個(gè)OneWay的單向調(diào)用,也只能等它處理完后才會(huì)接著處理后面的SayHello請(qǐng)求,所以會(huì)卡5秒。
并發(fā)模式有以下三種,MSDN上的介紹有點(diǎn)復(fù)雜,我給簡(jiǎn)化一下:
Single:?jiǎn)尉€程調(diào)用,請(qǐng)求只能一個(gè)一個(gè)處理; Reentrant:可重入的單線程調(diào)用,本質(zhì)仍是單線程,處理回調(diào)時(shí),回調(diào)請(qǐng)求會(huì)進(jìn)入隊(duì)列尾部排隊(duì); Multiple:多線程調(diào)用,請(qǐng)求是并發(fā)的響應(yīng)的; |
調(diào)置服務(wù)并發(fā)模型是在契約的實(shí)現(xiàn)類上,我們?yōu)镈ataService類加一個(gè)Attribute:
- ///
- /// 用ServiceBehavior為契約實(shí)現(xiàn)類標(biāo)定行為屬性,此處指定并發(fā)模型為ConcurrencyMode.Multiple,即并發(fā)訪問
- ///
- [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
- public class DataProvider : IData
- {
- //略
- }
這回再編譯運(yùn)行一下,連續(xù)打出了2行 Hello WCF,中間不再阻塞了。
現(xiàn)在我們?cè)賮?lái)看看雙向通訊的問題。雙向通訊可以基于HTTP、TCP、Named Pipe、MSMQ,但要注意,basicHttpBinding和wsHttpBinding不行,要換用wsDualHttpBinding,它會(huì)創(chuàng)建兩個(gè)連接來(lái)進(jìn)行雙向通訊。至于TCP,它天然就是雙向通訊的。
1、服務(wù)端
服務(wù)契約要進(jìn)行修改,增加關(guān)于回調(diào)的契約:
- using System;
- using System.ServiceModel;
- using System.ServiceModel.Description;
- namespace Server
- {
- ///
- /// 增加了CallbackContract的標(biāo)識(shí),用于指明針對(duì)此服務(wù)契約的回調(diào)契約是IDataCallback
- ///
- [ServiceContract(Namespace = "WCF.Demo", CallbackContract = typeof(IDataCallback))]
- public interface IData
- {
- [OperationContract]
- string SayHello(string userName);
- [OperationContract(IsOneWay = true)]
- void Sleep();
- }
- ///
- /// 定義服務(wù)回調(diào)契約,注意它沒有契約標(biāo)識(shí),只是個(gè)一般接口
- ///
- public interface IDataCallback
- {
- ///
- /// 定義一個(gè)回調(diào)方法,由于回調(diào)不可能要求對(duì)方再響應(yīng),所以也標(biāo)識(shí)成OneWay的調(diào)用,同樣不需要有返回值
- ///
- [OperationContract(IsOneWay = true)]
- void SleepCallback(string text);
- }
- }
對(duì)應(yīng)的契約實(shí)現(xiàn)類要修改一下:
- 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來(lái)獲取指定類型的回調(diào)對(duì)象
- var callback = OperationContext.Current.GetCallbackChannel
(); - //回調(diào)SleepCallback方法,并傳遞參數(shù)
- callback.SleepCallback("睡醒了");
- }
- }
- }
2、客戶端
仍然提醒一下別忘了把新的服務(wù)契約更新到客戶端??蛻舳说恼{(diào)用要調(diào)整一下:
- using System;
- using System.ServiceModel;
- using System.ServiceModel.Channels;
- namespace Client
- {
- class Program
- {
- static void Main(string[] args)
- {
- //定義一個(gè)實(shí)現(xiàn)回調(diào)接口的類實(shí)例
- var context = new DataCallbackImp();
- //創(chuàng)建代理的時(shí)候變了,要用DuplexChannelFactory,因?yàn)镮Data契約已經(jīng)標(biāo)識(shí)了有回調(diào),所以必須要用支持雙向通訊的ChannelFactory,傳入剛才創(chuàng)建的回調(diào)實(shí)例
- var proxy = new DuplexChannelFactory
(context, "DataService").CreateChannel(); - //調(diào)用Sleep
- proxy.Sleep();
- //調(diào)用SayHello方法
- Console.WriteLine(proxy.SayHello("WCF"));
- //等待按任意鍵,先不要關(guān)連接
- Console.ReadKey();
- ((IChannel)proxy).Close();
- }
- ///
- /// 實(shí)現(xiàn)回調(diào)接口中的類,圖省事寫到這里了
- ///
- class DataCallbackImp : Server.IDataCallback
- {
- ///
- /// 實(shí)現(xiàn)SleepCallback方法
- ///
- public void SleepCallback(string text)
- {
- Console.WriteLine("收到回調(diào)了:" + text);
- }
- }
- }
編譯運(yùn)行,屏幕先顯示一行“Hello WCF.”,過5秒后顯示“收到回調(diào)了:睡醒了”。