第六篇:单向与双向通讯
项目开发中我们时常会遇到需要异步调用的问题,有时忽略服务端的返回值,有时希望服务端在需要的时候回调,今天就来看看在WCF中如何实现。
先看不需要服务端返回值的单向调用,老规矩,直接上代码,再解释。
契约接口中增加一个Sleep方法:
- using System;
- using System.ServiceModel;
- using System.Text;
-
- namespace Server
- {
- [ServiceContract(Namespace="WCF.Demo")]
- public interface IData
- {
- [OperationContract]
- string SayHello(string userName);
-
-
-
-
- [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);
- }
-
-
-
-
- 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();
-
-
- Console.WriteLine(proxy.SayHello("WCF"));
-
- proxy.Sleep();
-
- Console.WriteLine(proxy.SayHello("WCF"));
-
- ((IChannel)proxy).Close();
- }
- }
按我们的设想,两次SayHello调用之间应该没有延迟,因为Sleep是异步的嘛,编译运行一下,结果…… 中间卡住了5秒,这是为什么呢?
这其中涉及到一个并发模型的问题,默认情况下,WCF以单线程模型对外提供服务,也就是说,只能一个一个处理请求,即使是一个OneWay的单向调用,也只能等它处理完后才会接着处理后面的SayHello请求,所以会卡5秒。
并发模式有以下三种,MSDN上的介绍有点复杂,我给简化一下:
Single:单线程调用,请求只能一个一个处理; Reentrant:可重入的单线程调用,本质仍是单线程,处理回调时,回调请求会进入队列尾部排队; Multiple:多线程调用,请求是并发的响应的; |
调置服务并发模型是在契约的实现类上,我们为DataService类加一个Attribute:
-
-
-
- [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
- public class DataProvider : IData
- {
-
- }
这回再编译运行一下,连续打出了2行 Hello WCF,中间不再阻塞了。
现在我们再来看看双向通讯的问题。双向通讯可以基于HTTP、TCP、Named Pipe、MSMQ,但要注意,basicHttpBinding和wsHttpBinding不行,要换用wsDualHttpBinding,它会创建两个连接来进行双向通讯。至于TCP,它天然就是双向通讯的。
服务契约要进行修改,增加关于回调的契约:
- using System;
- using System.ServiceModel;
- using System.ServiceModel.Description;
-
- namespace Server
- {
-
-
-
- [ServiceContract(Namespace = "WCF.Demo", CallbackContract = typeof(IDataCallback))]
- public interface IData
- {
- [OperationContract]
- string SayHello(string userName);
-
- [OperationContract(IsOneWay = true)]
- void Sleep();
- }
-
-
-
-
- public interface IDataCallback
- {
-
-
-
- [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);
-
-
- var callback = OperationContext.Current.GetCallbackChannel<IDataCallback>();
-
- callback.SleepCallback("睡醒了");
- }
- }
- }
仍然提醒一下别忘了把新的服务契约更新到客户端。客户端的调用要调整一下:
- using System;
- using System.ServiceModel;
- using System.ServiceModel.Channels;
-
- namespace Client
- {
- class Program
- {
- static void Main(string[] args)
- {
-
- var context = new DataCallbackImp();
-
- var proxy = new DuplexChannelFactory<Server.IData>(context, "DataService").CreateChannel();
-
-
- proxy.Sleep();
-
- Console.WriteLine(proxy.SayHello("WCF"));
-
-
- Console.ReadKey();
- ((IChannel)proxy).Close();
- }
-
-
-
-
- class DataCallbackImp : Server.IDataCallback
- {
-
-
-
- public void SleepCallback(string text)
- {
- Console.WriteLine("收到回调了:" + text);
- }
- }
- }
编译运行,屏幕先显示一行“Hello WCF.”,过5秒后显示“收到回调了:睡醒了”。
本文转自 BoyTNT 51CTO博客,原文链接:http://blog.51cto.com/boytnt/803655,如需转载请自行联系原作者