WCF除了支持经典的请求应答(Request-Reply)模式外,还提供了什么操作调用模式,他们有什么不同以及我们如何在开发中使用这些操作调用模式。今天本节文章里会详细介绍。WCF分布式开发步步为赢(10):请求应答(Request-Reply)、单向操作(One-Way)、回调操作(Call Back).本文结构:【1】请求应答(Request-Reply)、【2】单向操作(One-Way)、【3】回调操作(Call Back)、【4】示例代码分析、【5】总结。最后上传本文的示例代码。

WCF除了支持经典的请求/应答模式意外,还提供了对单向操作、双向回调操作模式的支持,此外还有流操作(后者与WSE3.0提供的优化传输机制类似,我曾经在这个文章里进行过讲解WSE3.0构建Web服务安全(4):MTOM消息传输优化和文件上传、下载 )。今天我们会介绍几种操作调用模式的概念,区别,实现机制,以及如何在代码中实现他们,最后给出的要注意的细节问题。

【1】请求应答(Request-Reply):

请求应答模式是默认的操作模式。这与经典的C/S编程类似,客户端发送请求,阻塞客户端进程,服务端返回操作结果。请求应答模式与绑定对应关系 :

  1. 绑定协议名称            支持可靠性         默认可靠性           支持有序传递       请求应答模式
  2. BasicHttpBinding                No               N/A                    No                  Yes
  3. NetTcpBinding                   Yes               Off                    Yes                  Yes
  4. NetPeerTcpBinding              No                N/A                   No                   No
  5. NetNamedPipeBinding          No                N/A (On)           Yes                  Yes
  6. WSHttpBinding                  Yes               Off                    Yes                  Yes
  7. WSFederationHttpBinding   Yes               Off                     Yes                  Yes
  8. WSDualHttpBinding            Yes               On                     Yes                  Yes
  9. NetMsmqBinding                 No                N/A                    No                   No
  10. MsmqIntegrationBinding       No               N/A                    No                   Yes

除了NetPeerTcpBinding和NetMsmqBinding绑定,所有的绑定均支持请求-应答操作。

【2】单向操作(One-Way):

【2.1】概念:

简单来说,单向操作没有返回值,客户端只管调用,不管结果。单向操作客户端一旦发出请求,WCF会生成一个请求,不会给客户端返回任何消息。单向操作不同于异步操作,虽然单向操作只是在发出调用的瞬间阻塞客户端,但如果发出多个单向调用,WCF会将请求调用放入队列,并在某个时候执行。队列存储调用的个数是有限的,一旦发出的调用个数超出了队列存储调用的设置值,则会发生阻塞现象,因为调用无法放入队列。当队列的请求出列后,产生阻塞的调用就会放入队列,并解除对客户端的阻塞。绑定协议与单向请求模式关系:

      • 绑定协议名称            支持可靠性         默认可靠性           支持有序传递       单向模式
      • BasicHttpBinding                No               N/A                    No                  Yes
      • NetTcpBinding                   Yes               Off                    Yes                  Yes
      • NetPeerTcpBinding              No                N/A                   No                   Yes
      • NetNamedPipeBinding          No                N/A (On)           Yes                  Yes
      • WSHttpBinding                  Yes               Off                    Yes                  Yes
      • WSFederationHttpBinding   Yes               Off                     Yes                  Yes
      • WSDualHttpBinding            Yes               On                     Yes                  Yes
      • NetMsmqBinding                 No                N/A                    No                   Yes
      • MsmqIntegrationBinding       No               N/A                    No                   Yes

        和请求应答模式不同。所有的WCF绑定通信协议都支持单向操作。 
        【2.2】实现方式:
            配置单向操作的方式也很简单,WCF的OperationContract 定义了IsOneWay属性。我们设置设置单向操作的方法是利用OperationContract特性的IsOneWay属性,例如:

        1.         //操作契约,单调操作,不返回应答消息,会话服务中,保证是最后一个操作
        2.         [OperationContract(IsOneWay=true,IsInitiating=false,IsTerminating=true)]//
        3.         void SayHello2(string name);

        单向操作配置的属性定义在操作契约级别上。而不是用在服务契约级别。

        【2.3】单向操作小节:

        (1)被设置为单向操作的方法不能包含返回值,即它的返回值只能为void,否则会抛出InvalidOperationException异常。 
         (2)在会话契约中虽然允许定义单向操作([ServiceContract( SessionMode =SessionMode.Required, Namespace = "http://www.cnblogs.com/frank_xl/")]),但由于单向操作服务端管理客户端会话状态十分困难,因而,单向操作的最佳适用场景是在单调服务或单例服务中。如果在会话契约中定义了单向操作,就必须保证单向操作是终止会话的最后一个操作,返回void类型值。这可以通过分步操作来实现。代码如下:

        1.  
        2.     //1.单向服务契约,会话服务
        3.     [ServiceContract( SessionMode =SessionMode.Required, Namespace = "http://www.cnblogs.com/frank_xl/")]
        4.     public interface IWCFServiceOneWay
        5.     {
        6.         //操作契约,单调操作,不返回应答消息,会话服务中,保证是最后一个操作
        7.         [OperationContract(IsOneWay=true,IsInitiating=false,IsTerminating=true)]//
        8.         void SayHello2(string name);
        9.         //操作契约,
        10.         [OperationContract]
        11.         string SayHello1(string name);
        12.  
        13.     }

        (3)如果因为通信(地址宿主)问题,调用操作失败,单向操作如果抛出异常;客户端受服务端异常影响,取决于实例模式以及使用绑定。

        【3】回调操作(Call Back):

        【3.1】概念:

        回调不是一个新的概念,早在C语言里就有过,C#里更是有委托实现回调机制。软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础,因此,下面我们着重讨论回调机制在WCF软件架构中的实现。回调机制如图所示:

            并非所有的绑定协议都支持回调,http本质上是无连接的协议,TCP/IP协议才会在客户端和服务端维持通信信道。两者之间的对应关系如下:

      • 绑定协议名称            支持可靠性         默认可靠性           支持有序传递       回调模式
      • BasicHttpBinding                No               N/A                    No                  No
      • NetTcpBinding                   Yes               Off                    Yes                  Yes
      • NetPeerTcpBinding              No                N/A                   No                   No
      • NetNamedPipeBinding          No                N/A (On)           Yes                  Yes
      • WSHttpBinding                  Yes               Off                    Yes                  No
      • WSFederationHttpBinding   Yes               Off                     Yes                  No
      • WSDualHttpBinding            Yes               On                     Yes                  Yes
      • NetMsmqBinding                 No                N/A                    No                   No
      • MsmqIntegrationBinding       No               N/A                    No                   No

        BasicHttpBinding,WSHttpBinding绑定协议不支持回调操作。NetTcpBinding和NetNamedPipeBinding绑定支持回调操作;具有可靠消息传输的WSDualHttpBinding绑定是通过设置两个HTTP信道来支持双向通信。

      • 【3.2】实现代码:
           一个服务契约只能包含一个回调契约。通过ServiceContract特性,可以指定回调契约:

        1.  
        2.     //0.回调服务契约,由于回调方法在客户端执行,因此无须添加 ServiceContractAttribute。对于回调操作,服务器无须获取其返回信息,因此添加 IsOneWay=true 特性参数。
        3.     public interface IWCFServiceCallBack
        4.     {
        5.         //操作契约
        6.         [OperationContract(IsOneWay=true)]//
        7.         void SayHelloCalllBack();
        8.     }
        9.     //1.服务契约,指定 SessionMode 和回调类型。
        10.     [ServiceContract(SessionMode = SessionMode.Required,CallbackContract = typeof(IWCFServiceCallBack))]
        11.     public interface IWCFService
        12.     {
        13.         //操作契约,
        14.         [OperationContract]
        15.         string SayHelloToUser(string name);
        16.  
        17.     }

        回调契约无须标记ServiceContract特性,但是在回调契约中必须为服务的操作标记OperationContract特性。 
        在导入回调契约的元数据中,回调契约以Callback结尾。服务端反序列化本地代码的时候会生成客户端回调操作契约Callback后缀。

        【3.3】回调小节:
        (1)如果使用了回调契约,回调契约不需要ServiceContract特性,设置为回调契约就默认了服务契约的特性。
        (2)客户端通过回调传递给服务端的消息包含了回调契约终结点的引用。在服务端,可以通过OperationContext类的泛型方法GetCallbackChannel<T>()获得。代码如下:

        1.         //获取客户端通道实例
        2.         IWCFServiceCallBack callback = OperationContext.Current.GetCallbackChannel<IWCFServiceCallBack>();

        【4】示例代码分析:

        直接看概念还不能很好的理解回调的机制,下面我们来具体看看WCF里如何实现回调。客户端调用服务操作,服务操作通过客户端上下文实例调用客户端操作,这是回调操作的基本过程。一下是具体的代码实现讲解过程。这里只介绍回调操作的具体实现代码。单向操作过于简单,注释也比较详细,大家可以参考上传的代码。

        【4.1】服务端:

        定义一个回调契约IWCFServiceCallBack,服务契约IWCFService、服务类WCFService : IWCFService继承服务契约。代码如下:

        1.  
        2.  //1.回调服务契约,由于回调方法在客户端执行,因此无须添加 ServiceContractAttribute。对于回调操作,服务器无须获取其返回信息,因此添加 IsOneWay=true 特性参数。
        3.     public interface IWCFServiceCallBack
        4.     {
        5.         //操作契约
        6.         [OperationContract()]//
        7.         void SayHelloCalllBack();
        8.     }
        9.     //2.服务契约,指定 CallbackContract 回调契约。
        10.     [ServiceContract(CallbackContract = typeof(IWCFServiceCallBack))]
        11.     public interface IWCFService
        12.     {
        13.         //操作契约,
        14.         [OperationContract]
        15.         string SayHelloToUser(string name);
        16.  
        17.     }
        18.     //3.服务类,继承接口。实现服务契约定义的操作
        19.     public class WCFService : IWCFService
        20.     {
        21.         //获取当前操作客户端对象实例
        22.         IWCFServiceCallBack callback = OperationContext.Current.GetCallbackChannel<IWCFServiceCallBack>();
        23.  
        24.         //实现接口定义的方法
        25.         public string SayHelloToUser(string name)
        26.         {
        27.             //Action<IWCFServiceCallBack> invoke = delegate(IWCFServiceCallBack callBack)
        28.             //{ callBack.SayHelloCalllBack(); };
        29.             ////callback(invoke);
        30.             Console.WriteLine("Hello! {0} , ", name);
        31.             callback.SayHelloCalllBack();
        32.             return "Hello! " + name;
        33.         }
        34.     }

        服务端 获取当前操作客户端对象实例
                IWCFServiceCallBack callback = OperationContext.Current.GetCallbackChannel<IWCFServiceCallBack>();callback.SayHelloCalllBack();执行回调客户端当前实例方法。

        【4.2】宿主:

        宿主启动和绑定节点配置和前面几节讲解的配置过程类似。这里配置的协议是TCP。配置文件代码如下:

        1.  
        2. <service   behaviorConfiguration="WCFService.WCFServiceBehavior" name="WCFService.WCFService">
        3.         <endpoint
        4.           address="net.tcp://localhost:9004/WCFService"
        5.           binding="netTcpBinding"
        6.           contract="WCFService.IWCFService">
        7.         </endpoint>
        8.         <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        9.         <host>
        10.           <baseAddresses>
        11.             <add baseAddress="http://localhost:9003/"/>
        12.             <add baseAddress="net.tcp://localhost:9004/"/>
        13.           </baseAddresses>
        14.         </host>
        15.       </service>

        【4.3】客户端:

        运行服务托管宿主,客户端添加服务引用,反序列化服务元数据,如图:

        修改客户端代码,重新实现回调契约的操作方法,如下:

        1.  
        2.     [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
        3.     public class WCFServiceCallback : IWCFServiceCallback
        4.     {
        5.  
        6.         public void SayHelloCalllBack()
        7.         {
        8.             Console.WriteLine("Client method is CallBacking");
        9.         }  
        10.     }

        测试回调代码,我们实例化一个回调类的实例,然后作为上下文实例的参数。最后把上下文作为参数实例化一个客户端代理。具体代码如下:

        1.  
        2.             //CallBack 回调服务
        3.             Console.WriteLine("Call Back Operation Test");
        4.             WCFClientCallBack.IWCFServiceCallback callBack = new WCFClientCallBack.WCFServiceCallback();
        5.  
        6.             InstanceContext context = new InstanceContext(callBack);
        7.             WCFClientCallBack.WCFServiceClient WCFServiceCallBackClientProxy = new WCFClientCallBack.WCFServiceClient(context, "NetTcpBinding_IWCFService");
        8.             //通过代理调用调用SayHelloToUser,传递对象
        9.             Console.WriteLine(WCFServiceCallBackClientProxy.SayHelloToUser("Frank Xu Lei Call Back"));

        【4.4】运行结果:

        这里的运行结果包括单向操作和回调操作结果,客户端调用一个服务操作,服务操作再通过客户端上下文实例引用调用客户端操作。成功执行回调操作。结果如图:

        【5】总结:

        (1) 服务对回调的调用可能会产生死锁。就是指当回调的应答消息也需要获得与服务实例关联的相同的锁时,会导致死锁。此时服务线程已经被阻塞,服务操作正在等待回调操作执行完毕,而回调操作却又在等待服务释放锁。 解决死锁的办法:<1>将服务配置为允许多线程访问,会增加服务开发者负担。

        <2>将回调设置为重入(Reentrancy)    //3.服务类,继承接口。实现服务契约定义的操作
            [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]
            public class WCFService : IWCFService
            {...}

      • 。所谓“重入”,是指对同步域拥有独占访问权的线程A调用了同步域之外对象的方法,此时,另外的线程B若要访问该同步域,则线程A将释放对同步域的锁,允许线程B进入。直到线程B执行完毕并释放对同步域的锁后,线程A将重新进入该同步域。由于服务被配置为重入,则服务调用回调引用时会释放锁。然后将回调返回给客户端,控制权则返回给服务,服务会重入并重新获取锁。 
        <3>将回调操作设置为单向操作(        [OperationContract(IsOneWay=true)]// void SayHelloCalllBack();)。此时,回调调用不会产生应答消息,服务操作一旦执行了回调操作,就会继续执行,回调对象不会争用与服务实例关联的锁,从而解决了死锁问题。

        (2)单调服务的回调问题需要考虑回调对象的引用存储问题,因为每次调用结束都会释放服务实例对象,客户端的状态也会丢失。

        (3)单例服务的问题是,犹豫所有的服务共享一个实例,而其生命周期的问题,回调的引用计数会增加。

        (4)回调契约的层级问题,一旦一个服务契约提供了回调契约的定义,其所有的子接口必须生命和其一致的回调契约。

        (5)NetTcpBinding和NetNamedPipeBinding绑定支持回调操作,并且和客户端共享一个通道端口;具有可靠消息传输的WSHttpBinding需要维护连个端口有可能产生端口冲突,编程时值得注意。

        以上就是本节的全部内容,下一节我们会介绍WCF流操作的一些内容,这里也值得学习。因为在前面的WSE3.0文章里我介绍了WSE优化文件传输的问题。当时也提到了流操作的概念。WCF框架也提供了流操作的支持,同样值得我们学习。这里对大规模数据对象的操作和处理有重要作用,下一个准备介绍一下。最后上传本文的示例代码供大家参考/Files/frank_xl/WCFServiceOperationFrankXuLei20090503.rar

      • 参考文章:

        1.Programming WCF Services

        2.WSE3.0构建Web服务安全(4):MTOM消息传输优化和文件上传、下载

        3.什么叫回调函数,http://hi.baidu.com/zhuyipeng/blog/item/863fefdb7c736c63d1164eec.htm

WCF分布式开发步步为赢(10):请求应答(Request-Reply)、单向操作(One-Way)、回调操作(Call Back).的更多相关文章

  1. WCF分布式开发步步为赢(11):WCF流处理(Streaming)机制

    WSE3.0框架提供了数据优化传输机制,WSE3.0构建Web服务安全(4):MTOM消息传输优化和文件上传.下载 疑问里进行了介绍.WCF同样也提供了流操作来支持大数据对象的传输和处理优化机制,今天 ...

  2. WCF分布式开发步步为赢(4):WCF服务可靠性传输配置与编程开发

    今天继续WCF分布式开发步步为赢系列的第4节:WCF服务可靠性传输配置与编程开发.这个章节,我们要介绍什么是WCF服务的可靠性传输,随便介绍网络协议的概念,Web Service为什么不支持可靠性传出 ...

  3. WCF分布式开发步步为赢(3)WCF服务元数据交换、配置及编程开发

    今天我们继续WCF分布式开发步步为赢(3)WCF服务元数据交换.配置及编程开发的学习.经过前面两节的学习,我们了解WCF分布式开发的相关的基本的概念和自定义宿主托管服务的完整的开发和配置过程.今天我们 ...

  4. WCF分布式开发步步为赢(12):WCF事务机制(Transaction)和分布式事务编程

    今天我们继续学习WCF分布式开发步步为赢系列的12节:WCF事务机制(Transaction)和分布式事务编程.众所周知,应用系统开发过程中,事务是一个重要的概念.它是保证数据与服务可靠性的重要机制. ...

  5. WCF分布式开发步步为赢(8):使用数据集(DataSet)、数据表(DataTable)、集合(Collection)传递数据

    数据集(DataSet).数据表(DataTable).集合(Collection)概念是.NET FrameWork里提供数据类型,在应用程序编程过程中会经常使用其来作为数据的载体,属于ADO.NE ...

  6. WCF分布式开发步步为赢(15):错误契约(FaultContract)与异常处理(ExceptionHandle)

    今天学习WCF分布式开发步步为赢系列的15节:错误契约(FaultContract)与异常处理(ExceptionHandle).本节内容作为WCF分布式开发的一个重要知识点,无论在学习还是项目中都应 ...

  7. WCF分布式开发步步为赢(13):WCF服务离线操作与消息队列MSMQ

    之前曾经写过一个关于MSMQ消息队列的文章:WCF分布式开发必备知识(1):MSMQ消息队列 ,当时的目的也是用它来作为学习WCF 消息队列MSMQ编程的基础文章.在那篇文章里,我们详细介绍了MSMQ ...

  8. WCF分布式开发步步为赢(7):WCF数据契约与序列化

    本节继续学习WCF分布式开发步步为赢(7):WCF数据契约与序列化.数据契约是WCF应用程序开发中一个重要的概念,毫无疑问实现客户端与服务端数据契约的传递中序列化是非常重要的步骤.那么序列化是什么?为 ...

  9. WCF分布式开发步步为赢(5)服务契约与操作重载

    继上一节WCF分布式开发步步为赢系列的(4):WCF服务可靠性传输配置与编程开发,本节我们继续学习WCF分布式开发步步为赢的第(5)节:服务契约与操作重载.这里我们首先讲解OOP面向对象的编程中方法重 ...

随机推荐

  1. 随机数范围扩展(如rand7()到rand10())(转)

    题目:已知有个rand7()的函数,返回1到7随机自然数,让利用这个rand7()构造rand10() 随机1~10.分析:要保证rand10()在整数1-10的均匀分布,可以构造一个1-10*n的均 ...

  2. Fragment+RadioButton实现点击切换页面效果

    首先我们需要在主布局文件中 放一个 容器,方便让fragment加入进去,我们创建了四个Fragment,并用RedioButton实现了导航栏 MainActivity.java package c ...

  3. 微信扫码支付asp.net(C#)实现步骤

    支付提交页面: [HttpPost] public ActionResult index(decimal amount) { //生成订单10位序列号,此处用时间和随机数生成,商户根据自己调整,保证唯 ...

  4. windows CE 6.0编译报BLDDEMO: There were errors building MY283错误解决办法

    今天开始正式进入windows ce程序开发. 第一次编译windows ce6.0的系统,25分钟编译后报:BLDDEMO: There were errors building MY283 错误. ...

  5. jQuery多库共存处理

    jQuery多库共存处理(来自慕课网) 多库共存换句话说可以叫无冲突处理. 总的来说会有2种情况会遇到: 1.$太火热,jQuery采用$作为命名空间,不免会与别的库框架或者插件相冲突. 2.jQue ...

  6. nodejs笔记五--MongoDB基本环境配置及增删改查;

    一.基本环境配置: 1,首先到官网(http://www.mongodb.org/downloads )下载合适的安装包,然后一步一步next安装,当然可以自己更改安装目录:安装完成之后,配置环境变量 ...

  7. source insight 完全卸载和重装

    Source insight的卸载不干净,会影响之后的安装 切入正题,完美卸载source insight的方法: 一.在pc的控制面板—>程序—>卸载程序 找到source insigh ...

  8. 如何ping通两台计算机

    如何ping通两台计算机 因为ping是基于IP协议的,所以,先要保证两台计算机在同一个子网中,这里涉及到vlan和子网的概念 若两台主机不在同一个子网中则无法ping通 若两台主机在同一个子网中却p ...

  9. 缓存应用--Memcached分布式缓存简介

    一.   什么是Memcached Memcached 是一个高性能的分布式内存 对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象 来减少读取数据库的次数,从而提供动态. ...

  10. bnuoj 34985 Elegant String DP+矩阵快速幂

    题目链接:http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=34985 We define a kind of strings as elegant s ...