WCF简单教程(6) 单向与双向通讯
第六篇:单向与双向通讯
项目开发中我们时常会遇到需要异步调用的问题,有时忽略服务端的返回值,有时希望服务端在需要的时候回调,今天就来看看在WCF中如何实现。
先看不需要服务端返回值的单向调用,老规矩,直接上代码,再解释。
1、服务端
契约接口中增加一个Sleep方法:
usingSystem;usingSystem.ServiceModel;usingSystem.Text;namespaceServer{[ServiceContract(Namespace="WCF.Demo")]publicinterfaceIData{[OperationContract]stringSayHello(stringuserName);///<summary>///IsOneWay=true表明这是一个单向调用,注意返回值是void,因为既然是单向调用,客户端肯定不会等待接收返回值的///</summary>[OperationContract(IsOneWay=true)]voidSleep();}}
对应的实现类中,我们来实现这个方法:
usingSystem;usingSystem.Text;namespaceServer{publicclassDataProvider:IData{publicstringSayHello(stringuserName){returnstring.Format("Hello{0}.",userName);}///<summary>///实现Sleep方法,暂时不做任何事情,只是睡眠5秒///</summary>publicvoidSleep(){Thread.Sleep(5000);}}}
App.config就不再列出来了,里面用的是一个netTcpBinding的endpoint。
2、客户端
首先别忘了客户端的契约要与服务端保持一致,App.config也不列出来了,里面有对应的endpoint。主要是调用的代码:
usingSystem;usingSystem.ServiceModel;usingSystem.ServiceModel.Channels;namespaceClient{classProgram{staticvoidMain(string[]args){varproxy=newChannelFactory<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)]publicclassDataProvider:IData{//略}
这回再编译运行一下,连续打出了2行 Hello WCF,中间不再阻塞了。
现在我们再来看看双向通讯的问题。双向通讯可以基于HTTP、TCP、Named Pipe、MSMQ,但要注意,basicHttpBinding和wsHttpBinding不行,要换用wsDualHttpBinding,它会创建两个连接来进行双向通讯。至于TCP,它天然就是双向通讯的。
1、服务端
服务契约要进行修改,增加关于回调的契约:
usingSystem;usingSystem.ServiceModel;usingSystem.ServiceModel.Description;namespaceServer{///<summary>///增加了CallbackContract的标识,用于指明针对此服务契约的回调契约是IDataCallback///</summary>[ServiceContract(Namespace="WCF.Demo",CallbackContract=typeof(IDataCallback))]publicinterfaceIData{[OperationContract]stringSayHello(stringuserName);[OperationContract(IsOneWay=true)]voidSleep();}///<summary>///定义服务回调契约,注意它没有契约标识,只是个一般接口///</summary>publicinterfaceIDataCallback{///<summary>///定义一个回调方法,由于回调不可能要求对方再响应,所以也标识成OneWay的调用,同样不需要有返回值///</summary>[OperationContract(IsOneWay=true)]voidSleepCallback(stringtext);}}
对应的契约实现类要修改一下:
usingSystem;usingSystem.ServiceModel;usingSystem.ServiceModel.Description;usingSystem.Threading;usingSystem.Net;namespaceServer{[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)]publicclassDataProvider:IData{publicstringSayHello(stringuserName){string.Format("Hello{0}.",userName);}publicvoidSleep(){ //先睡5秒Thread.Sleep(5000); //用OperationContext.Current来获取指定类型的回调对象varcallback=OperationContext.Current.GetCallbackChannel<IDataCallback>(); //回调SleepCallback方法,并传递参数callback.SleepCallback("睡醒了");}}}
2、客户端
仍然提醒一下别忘了把新的服务契约更新到客户端。客户端的调用要调整一下:
usingSystem;usingSystem.ServiceModel;usingSystem.ServiceModel.Channels;namespaceClient{classProgram{staticvoidMain(string[]args){//定义一个实现回调接口的类实例varcontext=newDataCallbackImp();//创建代理的时候变了,要用DuplexChannelFactory,因为IData契约已经标识了有回调,所以必须要用支持双向通讯的ChannelFactory,传入刚才创建的回调实例varproxy=newDuplexChannelFactory<Server.IData>(context,"DataService").CreateChannel();//调用Sleepproxy.Sleep();//调用SayHello方法Console.WriteLine(proxy.SayHello("WCF"));//等待按任意键,先不要关连接Console.ReadKey();((IChannel)proxy).Close();}///<summary>///实现回调接口中的类,图省事写到这里了///</summary>classDataCallbackImp:Server.IDataCallback{///<summary>///实现SleepCallback方法///</summary>publicvoidSleepCallback(stringtext){Console.WriteLine("收到回调了:"+text);}}}
编译运行,屏幕先显示一行“Hello WCF.”,过5秒后显示“收到回调了:睡醒了”。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。