您好,登錄后才能下訂單哦!
第六篇:?jiǎn)蜗蚺c雙向通訊
項(xiàng)目開(kāi)發(fā)中我們時(shí)常會(huì)遇到需要異步調(diào)用的問(wèn)題,有時(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);
- /// <summary>
- /// IsOneWay = true 表明這是一個(gè)單向調(diào)用,注意返回值是void,因?yàn)榧热皇菃蜗蛘{(diào)用,客戶(hù)端肯定不會(huì)等待接收返回值的
- /// </summary>
- [OperationContract(IsOneWay = true)]
- void Sleep();
- }
- }
對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)中,我們來(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);
- }
- /// <summary>
- /// 實(shí)現(xiàn)Sleep方法,暫時(shí)不做任何事情,只是睡眠5秒
- /// </summary>
- public void Sleep()
- {
- Thread.Sleep(5000);
- }
- }
- }
App.config就不再列出來(lái)了,里面用的是一個(gè)netTcpBinding的endpoint。
2、客戶(hù)端
首先別忘了客戶(hù)端的契約要與服務(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<Server.IData>("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)該沒(méi)有延遲,因?yàn)镾leep是異步的嘛,編譯運(yùn)行一下,結(jié)果…… 中間卡住了5秒,這是為什么呢?
這其中涉及到一個(gè)并發(fā)模型的問(wèn)題,默認(rèn)情況下,WCF以單線(xiàn)程模型對(duì)外提供服務(wù),也就是說(shuō),只能一個(gè)一個(gè)處理請(qǐng)求,即使是一個(gè)OneWay的單向調(diào)用,也只能等它處理完后才會(huì)接著處理后面的SayHello請(qǐng)求,所以會(huì)卡5秒。
并發(fā)模式有以下三種,MSDN上的介紹有點(diǎn)復(fù)雜,我給簡(jiǎn)化一下:
Single:?jiǎn)尉€(xiàn)程調(diào)用,請(qǐng)求只能一個(gè)一個(gè)處理; Reentrant:可重入的單線(xiàn)程調(diào)用,本質(zhì)仍是單線(xiàn)程,處理回調(diào)時(shí),回調(diào)請(qǐng)求會(huì)進(jìn)入隊(duì)列尾部排隊(duì); Multiple:多線(xiàn)程調(diào)用,請(qǐng)求是并發(fā)的響應(yīng)的; |
調(diào)置服務(wù)并發(fā)模型是在契約的實(shí)現(xiàn)類(lèi)上,我們?yōu)镈ataService類(lèi)加一個(gè)Attribute:
- /// <summary>
- /// 用ServiceBehavior為契約實(shí)現(xiàn)類(lèi)標(biāo)定行為屬性,此處指定并發(fā)模型為ConcurrencyMode.Multiple,即并發(fā)訪(fǎng)問(wèn)
- /// </summary>
- [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
- public class DataProvider : IData
- {
- //略
- }
這回再編譯運(yùn)行一下,連續(xù)打出了2行 Hello WCF,中間不再阻塞了。
現(xiàn)在我們?cè)賮?lái)看看雙向通訊的問(wèn)題。雙向通訊可以基于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
- {
- /// <summary>
- /// 增加了CallbackContract的標(biāo)識(shí),用于指明針對(duì)此服務(wù)契約的回調(diào)契約是IDataCallback
- /// </summary>
- [ServiceContract(Namespace = "WCF.Demo", CallbackContract = typeof(IDataCallback))]
- public interface IData
- {
- [OperationContract]
- string SayHello(string userName);
- [OperationContract(IsOneWay = true)]
- void Sleep();
- }
- /// <summary>
- /// 定義服務(wù)回調(diào)契約,注意它沒(méi)有契約標(biāo)識(shí),只是個(gè)一般接口
- /// </summary>
- public interface IDataCallback
- {
- /// <summary>
- /// 定義一個(gè)回調(diào)方法,由于回調(diào)不可能要求對(duì)方再響應(yīng),所以也標(biāo)識(shí)成OneWay的調(diào)用,同樣不需要有返回值
- /// </summary>
- [OperationContract(IsOneWay = true)]
- void SleepCallback(string text);
- }
- }
對(duì)應(yīng)的契約實(shí)現(xiàn)類(lèi)要修改一下:
- 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)獲取指定類(lèi)型的回調(diào)對(duì)象
- var callback = OperationContext.Current.GetCallbackChannel<IDataCallback>();
- //回調(diào)SleepCallback方法,并傳遞參數(shù)
- callback.SleepCallback("睡醒了");
- }
- }
- }
2、客戶(hù)端
仍然提醒一下別忘了把新的服務(wù)契約更新到客戶(hù)端??蛻?hù)端的調(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)接口的類(lèi)實(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<Server.IData>(context, "DataService").CreateChannel();
- //調(diào)用Sleep
- proxy.Sleep();
- //調(diào)用SayHello方法
- Console.WriteLine(proxy.SayHello("WCF"));
- //等待按任意鍵,先不要關(guān)連接
- Console.ReadKey();
- ((IChannel)proxy).Close();
- }
- /// <summary>
- /// 實(shí)現(xiàn)回調(diào)接口中的類(lèi),圖省事寫(xiě)到這里了
- /// </summary>
- class DataCallbackImp : Server.IDataCallback
- {
- /// <summary>
- /// 實(shí)現(xiàn)SleepCallback方法
- /// </summary>
- public void SleepCallback(string text)
- {
- Console.WriteLine("收到回調(diào)了:" + text);
- }
- }
- }
編譯運(yùn)行,屏幕先顯示一行“Hello WCF.”,過(guò)5秒后顯示“收到回調(diào)了:睡醒了”。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。