🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 跟我一起学WCF(9)——WCF回调操作的实现 ## 一、引言 在上一篇文章中介绍了WCF对Session的支持,在这篇文章中将详细介绍WCF支持的操作。在WCF中,除了支持经典的请求/应答模式外,还提供了对单向操作、双向回调操作模式的支持,此外还有流操作的支持。接下来将详细介绍下这几种操作,并实现一个双向回调操作的例子。 ## 二、WCF操作详解 ## 2.1 请求—应答操作 请求应答模式是WCF中默认的操作模式。请求应答模式指的是:客户端以消息形式发送请求,它会阻塞客户端直到收到应答消息。应答的默认超时时间为1分钟,如果超过这一时间服务仍然没有应答,客户端就会获得一个TimeOutException异常。WCF中除了NetPeerTcpBinding和NetMsmqBinding绑定,所有的绑定都支持请求—应答操作。 ## 2.2 单向操作 单向操作是没有返回值的,客户端不关心调用是否成功。单向操作指的是:客户端一旦发出调用请求,WCF会生成一个请求消息发送给服务端,但客户端并不需要接收相关的应答消息。因此,单向操作不能有返回值,并且服务端抛出的任何异常都不会传递给客户端。所以客户端如果需要捕获服务端发生的异常,此时不能把操作契约的IsOneWay属性设置为true,该属性的默认值为false。异常处理参考:[如何在WCF进行Exception Handling](http://www.cnblogs.com/artech/archive/2007/06/15/784090.html)。单向操作不等同于异步操作,单向操作只是在发出调用的瞬间阻塞客户端,但如果发出多个单向调用,WCF会将请求调用放入服务端的队列中,并在某个时间进行执行。队列的存储个数有限,一旦发出的调用个数超出了队列容量,则会发生阻塞现象,此时调用请求无法放入丁烈,直到有其他请求被处理,即队列中的请求出队列后,产生阻塞的调用就会放入队列,并解除对客户端的阻塞。WCF中所有绑定都支持单向操作。WCF中实现单向操作只需要设置IsOneWay属性为true即可。这里需要注意一点:由于单向操作没有应答消息,因此它不能包含返回结果。 ## 2.3 回调操作 WCF支持服务将调用返回给它的客户端。在回调期间,服务成为了客户端,而客户端成为了服务。在WCF中,并不是所有的绑定都支持回调操作,只有具有双向能力的绑定才能够用于回调。例如,HTTP本质上是与连接无关的,所以它不能用于回调,因此我们不能基于basicHttpBinding和wsHttpBinding绑定使用回调,WCF为NetTcpBinding和NetNamedPipeBinding提供了对回调的支持,因为TCP和IPC协议都支持双向通信。为了让Http支持回调,WCF提供了WsDualHttpBinding绑定,它实际上设置了两个Http通道:一个用于从客户端到服务的调用,另一个用于服务到客户端的调用。 回调操作时通过回调契约来实现的,回调契约属于服务契约的一部分,一个服务契约最多只能包含一个回调契约。一旦定义了回调契约,就需要客户端实现回调契约。在WCF中,可以通过ServiceContract的[CallbackContract](http://msdn.microsoft.com/zh-cn/library/system.servicemodel.servicecontractattribute.callbackcontract(v=vs.110).aspx)属性来定义回调契约。具体的实现代码如下所示: ``` // 指定回调契约为ICallback [ServiceContract(Namespace="http://cnblog.com/zhili/", CallbackContract=typeof(ICallback))] public interface ICalculator { [OperationContract(IsOneWay = true)] void Multiple(double a, double b); } // 回调契约的定义,此时回调契约不需要应用ServiceContractAttribute特性 public interface ICallback { [OperationContract(IsOneWay = true)] void DisplayResult(double x, double y, double result); } ``` 在上面代码中,回调契约不必标记ServiceContract特性,因为类型只要被定义为回调契约,就代表它具有ServiceContract特性,但仍然需要为所有的回调接口中的方法标记OperationContract特性。 ## 2.4 流操作 在默认情况下,当客户端与服务交换消息时,这些消息会被放入到接收端的缓存中,一旦接收到完整的消息,就立即被传递处理。无论是客户端发送消息到服务还是服务返回消息给客户端,都是如此。当客户端调用服务时,只要接收到完整的消息,服务就会被调用,当包含了调用结果的返回消息被客户端完整接收时,才会接触对客户端的阻塞。对于数据量小的消息,这种交换模式提供了简单的编程模型,因为接收消息的耗时较短,然而,一旦处理数据量更大的消息,例如包含了多媒体内容或大文件,如果每次都要等到完整地接收消息之后才能解除阻塞,这未免也不现实。为了解决这样的问题,WCF允许接收端通过通道接收消息的同时,启动对消息数据的处理,这样的处理过程称为流传输模型。对于具有大量负载的消息而言,流操作改善了系统的吞吐量和响应速度,因为在发生和接收消息的同时,不管是发送端还是接收端都不会被阻塞。 ## 三、WCF中回调操作的实现 上面介绍了WCF中支持的四种操作,下面就具体看看WCF中回调操作的实现。该例子的基本原理是:客户端调用服务操作,服务操作通过客户端上下文实例调用客户端操作。下面还是按照三个步骤来实现该WCF程序。 第一步:同样是实现WCF服务契约和契约的实现。具体的实现代码如下所示: ``` 1 // 指定回调契约为ICallback 2 [ServiceContract(Namespace="http://cnblog.com/zhili/", CallbackContract=typeof(ICallback))] 3 public interface ICalculator 4 { 5 [OperationContract(IsOneWay = true)] 6 void Multiple(double a, double b); 7 } 8 9 // 回调契约的定义,此时回调契约不需要应用ServiceContractAttribute特性 10 public interface ICallback 11 { 12 [OperationContract(IsOneWay = true)] 13 void DisplayResult(double x, double y, double result); 14 } 15 16 // 服务契约的实现 17 public class CalculatorService : ICalculator 18 { 19 #region ICalculator Members 20 public void Multiple(double a, double b) 21 { 22 double result = a * b; 23 // 通过客户端实例通道 24 ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>(); 25 26 // 对客户端操作进行回调 27 callback.DisplayResult(a, b, result); 28 } 29 #endregion 30 } ``` 第二步:实现服务宿主。这里还是以控制台程序作为服务宿主。具体的实现代码如下所示: ``` 1 class Program 2 { 3 static void Main(string[] args) 4 { 5 using (ServiceHost host = new ServiceHost(typeof(CalculatorService))) 6 { 7 host.Opened += delegate 8 { 9 Console.WriteLine("Service start now...."); 10 }; 11 12 host.Open(); 13 Console.Read(); 14 } 15 } 16 } ``` 宿主对应的配置文件内容如下所示: ``` <configuration> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior> <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8080/Metadata"/> </behavior> </serviceBehaviors> </behaviors> <services> <service name="WCFContractAndService.CalculatorService"> <endpoint address="net.tcp://localhost:9003/CalculatorService" binding="**netTcpBinding**" contract="WCFContractAndService.ICalculator"/> </service> </services> </system.serviceModel> </configuration> ``` 第三步:实现客户端。由于服务端来对客户端操作进行回调,所以此时客户端需要实现回调契约。首先以管理员权限启动服务宿主,服务宿主启动成功之后,客户端通过添加服务引用的方式来生成客户端代理类,此时需要在添加服务引用窗口地址中输入:http://localhost:8080/Metadata。添加服务引用成功之后,接着在客户端实现回调契约,具体的实现代码如下所示: ``` 1 // 客户端中对回调契约的实现 2 public class CallbackWCFService : ICalculatorCallback 3 { 4 public void DisplayResult(double a, double b, double result) 5 { 6 Console.WriteLine("{0} * {1} = {2}", a, b, result); 7 } 8 } ``` 接下来就是实现测试回调操作的客户端代码了。具体的实现步骤是:实例化一个回调类的实例,然后把它作为上下文实例的操作,最后把上下文实例作为客户端代理的参数来实例化客户端代理。具体的实现代码如下所示: ``` 1 // 客户端实现,测试回调操作 2 class Program 3 { 4 static void Main(string[] args) 5 { 6 InstanceContext instanceContex = new InstanceContext(new CallbackWCFService()); 7 CalculatorClient proxy = new CalculatorClient(instanceContex); 8 proxy.Multiple(2,3); 9 10 Console.Read(); 11 } 12 } ``` 下面运行运行该程序来检测下该程序是否能够成功回调,首先以管理员权限启动服务宿主程序,再启动客户端程序,如果回调成功,你将看到如下图所示的运行结果: ![](https://box.kancloud.cn/2016-01-23_56a2eb495008a.png) 这里只是演示了回调操作的实现,关于流操作的实现,这里就不再去实现了,等具体需要的时候再去研究吧,同时给出关于流操作实现的参考文章: [Stream Operation in WCF](http://www.codeproject.com/Articles/36973/Stream-Operation-in-WCF) [WCF流处理(Streaming)机制](http://blog.csdn.net/frankxulei/article/details/4735907) ## 四、总结 到这里,WCF操作的内容就分享结束了,本文首先介绍了在WCF中支持四种操作:请求-应答操作、单向操作、回调操作和流操作,WCF中默认的操作时请求-应答操作,最后实现了一个回调操作的实例。 本文所有源码:[WCFCallbackOperation.zip](http://files.cnblogs.com/zhili/WCFCallbackOperation.zip)