WCF学习之旅—请求与答复模式和单向模式(十九)

四、HTTP双工模式

  双工模式建立在上文所实现的两种模式的基础之上,实现客户端与服务端相互调用:前面介绍的两种方法只是在客户端调用服务端的方法,然后服务端有返回值返回客户端;相互调用是指客户端调用服务端的方法,同时服务端也可以调用客户端的方法。

基于双工MEP (信息交换模式,Message Exchange Pattern,下同)消息交换可以看成是多个基本模式下 (比如请求-回复模式和单项模式)消息交换的组合。双工MEP又具有一些变体,比如典型的订阅-发布模式就可以看成是双工模式的一种表现形式。

    一) 两种典型的双工MEP

     1.请求过程中的回调

这是一种比较典型的双工消息交换模式的表现形式,客户端在进行服务调用的时候,附加上一个回调对象;服务在对处理该处理中,通过客户端附加的回调对 象(实际上是调用回调服务的代理对象)回调客户端的操作(该操作在客户端执行)。整个消息交换的过程实际上由两个基本的消息交换构成,其一是客户端正常的服务请求,其二则是服务端对客户端的回调。两者可以采用请求-回复模式,也可以采用单向(One-way)的MEP进行消息交换。图1描述了这样的过程,服务调用和回调都采用请求-回复。


图1 请求过程中的回调

     2.订阅-发布

订阅-发布模式是双工模式的一个典型的变体。在这个模式下,消息交换的双方变成了订阅者和发布者,若干订阅者就某个主题向发布者申请订阅,发布者将
所有的订阅者保存在一个订阅者列表中,在某个时刻将主题发送给该主题的所有订阅者。实际上基于订阅-发布模式的消息交换也可以看成是两个基本模式下消息交
换的组合,申请订阅是一个单向模式的消息交换(如果订阅者行为得到订阅的回馈,该消息交换也可以采用请求-回复模式);而主题发布也是一个基于单向模式的消息交换过程。订阅-发布消息交换模式如图2所示。

图2 订阅-发布

       二) 实例演示:创建基于双工通信的WCF应用

接下来我们通过一个的实例来学习基于双工通信的WCF应用。为简单起见,我们沿用上文( WCF学习之旅—请求与答复模式和单向模式(十九))的示例。在上文的示例中,我们都是调用
BookService直接显示书籍名称,并将结果通过Winform应用程序输出。在本例中我们将采用另外一种截然不同的方式调用服务并进行结果的输出:我们通过单向(One-way)的模式调用BookService(也就是客户端不可能通过回复消息得到结果),服务端在完成书籍名称显示之后,通过回调(Callback)的方式在客户端将书籍名称显示出来。

      步骤一:定义服务契约和回调契约

首先进行服务契约的定义,我们照例通过接口(IBookService)的方式定义服务契约,作用于指定显示书籍名称的方法DisplayName操作,我们通过OperationContractAttribute特性的IsOneway属性将操作定义成单向的操作,这意味着客户端仅仅是向服务端发送一个请求,并不会通过回复消息得到任何结果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace WcfServiceLib
{
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IBookService”。
[ServiceContract(CallbackContract = typeof(ICallback))]
public interface IBookService
{ /// <summary>
/// 单工模式,显示名称
/// </summary>
/// <param name="name">书籍名称</param>
[OperationContract(IsOneWay = true)]
void DisplayName(string name); } }

我们要实现的功能是通过在服务端回调客户端的操作方式实现结果的输出。客户端正常调用BookService的服务方法,那么在服务 执行过程中借助于客户端在服务调用时提供的回调对象对客户端的操作进行回调,从本质上讲是另外一种形式的服务调用。WCF采用基于服务契约的调用形式,客户端正常的服务调用需要服务契约,同理服务端回调客户端依然需要通过描述回调操作的服务契约,我们把这种服务契约称为回调契约。回调契约的类型通过ServiceContractAttribute特性的CallbackContract属性进行指定。

上面代码中服务契约IBookService的回调契约ICallback定义如下。由于回调契约本质也是一个服务契约,所以定义方式和一般意义上的 服务契约基本一样。有一点不同的是,由于定义IBookService的时候已经通过 [ServiceContract(CallbackContract=typeof(ICallback))]指明ICallback是一个服务契约 了,所以ICallback不再需要添加ServiceContractAttribute特性。ICallback定义了一个服务操作DisplayResult用于显示书籍名称与日期,由于服务端不需要回调的返回值,所以将回调操作也设为单向方法。ICallback接口代码如下。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace WcfServiceLib
{ public interface ICallback
{ [OperationContract(IsOneWay = true)]
void DisplayResult(string result);
} }

     步骤二:实现服务

在BookService实现了上面定义的服务契约IBookService中的方法,实现了DisplayName操作,完成书籍名称和日期的显示工作。同时把书籍名称与日期的显示通过回调的方式在客户端显示出来,所以需要借助于客户端提供的回调对象(该对象在客户端调用BookService的时候指定,在介绍客户端代码的实现的时候会讲到)。在WCF中,回调对象通过当前OperationContext的GetCallback<T>方法获得(T代表回调契约的类型)。代码如下。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text; namespace WcfServiceLib
{
// 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码、svc 和配置文件中的类名“BookService”。
// 注意: 为了启动 WCF 测试客户端以测试此服务,请在解决方案资源管理器中选择 BookService.svc 或 BookService.svc.cs,然后开始调试。
public class BookService : IBookService
{ /// <summary>
/// 双工模式,回调显示结果
/// </summary>
/// <param name="name">名称</param>
public void DisplayName(string name)
{ string result=string.Format("书籍名称:{0},日期时间{1}", name, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
Console.WriteLine("\r\n" + result);
ICallback call = OperationContext.Current.GetCallbackChannel<ICallback>();
call.DisplayResult("回调---"+result);
}
} }

注: OperationContext在WCF中是一个非常重要、也是一个十分有用的对象,它代表服务操作执行的上下文。我们可以通过静态属性 Current(OperationContext.Current)得到当前的OperationContext。借助 OperationContext,我们可以在服务端或者客户端获取或设置一些上下文,比如在客户端可以通过它为出栈消息(outgoing message)添加SOAP报头,以及HTTP报头(比如Cookie)等。在服务端,则可以通过OperationContex获取在客户端设置的 SOAP报头和HTTP报头。关于OperationContext的详细信息,可以参阅MSDN在线文档。

     步骤三:服务寄宿

我们通过一个控制台应用程序完成对BookService的寄宿工作,并将所有的服务寄宿的参数定义在代码中。由于双工通信依赖于一个双工的信道栈,即依赖于一个能够支持双工通信的绑定,在此我们选用了WSDualHttpBinding。

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks;
using WcfServiceLib; namespace ConsoleHosting
{ class Program
{
static void Main(string[] args)
{
//创建宿主的基地址
Uri baseAddress = new Uri("http://localhost:8080/BookService");
//创建宿主
using (ServiceHost host = new ServiceHost(typeof(BookService), baseAddress))
{
//向宿主中添加终结点
host.AddServiceEndpoint(typeof(IBookService), new WSDualHttpBinding (), baseAddress);
if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
{
//将HttpGetEnabled属性设置为true
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
behavior.HttpGetUrl = baseAddress;
//将行为添加到Behaviors中
host.Description.Behaviors.Add(behavior);
//打开宿主
host.Opened += delegate
{ Console.WriteLine("BookService控制台程序寄宿已经启动,HTTP监听已启动....,按任意键终止服务!");
}; host.Open();
//print endpoint information Console.ForegroundColor = ConsoleColor.Yellow;
foreach (ServiceEndpoint se in host.Description.Endpoints)
{
Console.WriteLine("[终结点]: {0}\r\n\t[A-地址]: {1} \r\n\t [B-绑定]: {2} \r\n\t [C-协定]: {3}",
se.Name, se.Address, se.Binding.Name, se.Contract.Name);
} Console.Read();
}
}
}
}
}

     注: 在WCF预定义绑定类型中,WSDualHttpBindingNetTcpBinding均提供了对双工通信的支持,但是两者在对双工通信的实现机制上却有本质的区别。WSDualHttpBinding是基于HTTP传输协议的;而HTTP协议本身是基于请求-回复的传输协议,基于HTTP的通道本质上都是单向的。WSDualHttpBinding实际上创建了两个通道,一个用于客户端向服务端的通信,而另一个则用于服务端到客户端的通信,从而间接地提供了双工通信的实现。而NetTcpBinding完全基于支持双工通信的TCP协议。

     步骤四:实现回调契约

在客户端程序为回调契约提供实现,在下面的代码中BookCallBack实现了回调契约ICallback,在DisplayResult方法中对书籍名称与日期的显示的输出。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace WinClient
{ class BookCallBack : BookServiceReference.IBookServiceCallback
{ //声明一个delegate(委托)类型:delegateDispalyResult,该类型可以搭载返回值为空,参数只有一个(string型)的方法。
public delegate void delegateDispalyResult(string result); //声明一个delegateDispalyResult类型的对象。该对象代表了返回值为空,参数只有一个(string型)的方法。它可以搭载N个方法。
public delegateDispalyResult mainThread; public void DisplayResult(string result)
{
mainThread(result);//通过委托的方法在主界面中显示书籍名称与日期信息
Console.WriteLine( result); }
}
}

     步骤五:服务调用

接下来实现对双工服务的调用,这是一个通过Web 服务引用,添加相应的WCF服务调用对象(关于如何添加Web服务引用请参考前面的文章)。在服务调用程序中,先创建回调对象,并通过InstanceContext对回调对象进行包装,然后把InstanceContext对象做为参数传递给服务调用对象。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; namespace WinClient
{ public partial class Form1 : Form
{ BookCallBack call; public Form1()
{ InitializeComponent();
//创建BookCallBack类的对象
BookCallBack call = new BookCallBack();
call.mainThread += new BookCallBack.delegateDispalyResult(DisplayResult);
instanceContext = new InstanceContext(call);
} InstanceContext instanceContext; private void buttonTwoWay_Click(object sender, EventArgs e)
{ BookServiceReference.BookServiceClient client = new BookServiceReference.BookServiceClient(instanceContext); //在BookCallBack对象的mainThread(委托)对象上搭载两个方法,在线程中调用mainThread对象时相当于调用了这两个方法。 textBox1.Text += string.Format("开始调用wcf服务:{0}\r\n\r\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
client.DisplayName("科学可以这样看丛书");
textBox1.Text += string.Format("\r\n\r\n调用结束:{0}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
} /// <summary>
/// 在界面上显示回调结果
/// </summary>
/// <param name="result"></param>
private void DisplayResult(string result)
{ //判断该方法是否被主线程调用,也就是创建labMessage1控件的线程,当控件的InvokeRequired属性为ture时,说明是被主线程以外的线程调用。如果不加判断,会造成异常
if (this.textBox1.InvokeRequired)
{ //再次创建一个BookCallBack类的对象
call = new BookCallBack();
//为新对象的mainThread对象搭载方法
call.mainThread += new BookCallBack.delegateDispalyResult(DisplayResult);
//this指窗体,在这调用窗体的Invoke方法,也就是用窗体的创建线程来执行mainThread对象委托的方法,再加上需要的参数(i)
this.Invoke(call.mainThread, new object[] { result });
}
else
{
textBox1.Text +="\r\n"+ result;
}
}
}
}

在服务寄宿程序启用的情况下,运行客户端程序后,通过服务端执行的运算结果会通过回调客户端的操作显示出来,下面是最终输出的结果。

WCF学习之旅—HTTP双工模式(二十)的更多相关文章

  1. WCF学习之旅—TCP双工模式(二十一)

    WCF学习之旅—请求与答复模式和单向模式(十九) WCF学习之旅—HTTP双工模式(二十) 五.TCP双工模式 上一篇文章中我们学习了HTTP的双工模式,我们今天就学习一下TCP的双工模式. 在一个基 ...

  2. WCF学习之旅—TcpTrace工具(二十六)

    止文(WCF学习之旅—TcpTrace工具(二十五))介绍了关于TcpTrance的一种使用方式,接下来介绍第二种使用方式. 三.通过ListenUri实现基于tcpTracer的消息路由 对于路由的 ...

  3. WCF学习之旅—TcpTrace工具(二十五)

    前面的几篇文章,我们学习了怎么开发WCF应用程序与服务,也学习了如何进行WCF的配置.对于Web Service与WCF服务应用,服务端与客户端的通信是通过收发SOAP Message进行,我们如何有 ...

  4. WCF学习之旅——第一个WCF示例(二)

    第四步:通过自我寄宿的方式寄宿服务 WCF服务需要依存一个运行着的进程(宿主),服务寄宿就是为服务指定一个宿主的过程.WCF是一个基于消息的通信框架,采用基于终结点(Endpoint)的通信手段. 终 ...

  5. WCF学习之旅—第三个示例之二(二十八)

    上接WCF学习之旅—第三个示例之一(二十七) 五.在项目BookMgr.Model创建实体类数据 第一步,安装Entity Framework 1)  使用NuGet下载最新版的Entity Fram ...

  6. WCF学习之旅—第三个示例之三(二十九)

    上接WCF学习之旅—第三个示例之一(二十七) WCF学习之旅—第三个示例之二(二十八) 在上一篇文章中我们创建了实体对象与接口协定,在这一篇文章中我们来学习如何创建WCF的服务端代码.具体步骤见下面. ...

  7. WCF学习之旅—WCF服务的WAS寄宿(十二)

    上接    WCF学习之旅—WCF服务部署到IIS7.5(九) WCF学习之旅—WCF服务部署到应用程序(十) WCF学习之旅—WCF服务的Windows 服务程序寄宿(十一) 八.WAS宿主 IIS ...

  8. WCF学习之旅—实现支持REST客户端应用(二十四)

    WCF学习之旅—实现REST服务(二十二) WCF学习之旅—实现支持REST服务端应用(二十三) 在上二篇文章中简单介绍了一下RestFul与WCF支持RestFul所提供的方法,及创建一个支持RES ...

  9. WCF学习之旅—实现支持REST服务端应用(二十三)

    在上一篇(WCF学习之旅—实现REST服务(二十二))文章中简单介绍了一下RestFul与WCF支持RestFul所提供的方法,本文讲解一下如何创建一个支持REST的WCF服务端程序. 四.在WCF中 ...

随机推荐

  1. DBImport V3.7版本发布及软件稳定性(自动退出问题)解决过程分享

    DBImport V3.7介绍: 1:先上图,再介绍亮点功能: 主要的升级功能为: 1:增加(Truncate Table)清表再插入功能: 清掉再插,可以保证两个库的数据一致,自己很喜欢这个功能. ...

  2. C#高性能TCP服务的多种实现方式

    哎~~ 想想大部分园友应该对 "高性能" 字样更感兴趣,为了吸引眼球所以标题中一定要突出,其实我更喜欢的标题是<猴赛雷,C#编写TCP服务的花样姿势!>. 本篇文章的主 ...

  3. OpenCASCADE Expression Interpreter by Flex & Bison

    OpenCASCADE Expression Interpreter by Flex & Bison eryar@163.com Abstract. OpenCASCADE provide d ...

  4. Eclipse中启动tomcat报错java.lang.OutOfMemoryError: PermGen space的解决方法

    有的项目引用了太多的jar包,或者反射生成了太多的类,异或有太多的常量池,就有可能会报java.lang.OutOfMemoryError: PermGen space的错误, 我们知道可以通过jvm ...

  5. JQuery 选择器

    选择器是JQuery的根基,在JQuery中,对事件的处理,遍历DOM和AJAX操作都依赖于选择器.如果能够熟练地使用选择器,不仅能简化代码,而且还可以事半功倍. JQuery选择器的优势 1.简洁的 ...

  6. js callee,caller学习

    原文地址:js callee,caller学习 /* * caller 返回一个对函数的引用,该函数调用了当前函数. * 如果函数是由顶层调用的,那么 caller包含的就是 null . * 如果在 ...

  7. Ajax使用WCF实现小票pos机打印源码

    通过ajax跨域方式调用WCF服务,实现小票pos机的打印,源码提供web方式,客户端方式测试,服务驻留右侧底部任务栏,可控制服务开启暂停,用户可自定义小票打印模板,配合零售录入. qq  22945 ...

  8. springmvc SSM 多数据源 shiro redis 后台框架 整合

    A集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单 下载地址    ; freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类 ...

  9. TabLayout + ViewPager

    一.实现思路 1.在build.gradle中添加依赖,例如: compile 'com.android.support:support-v4:23.4.0'compile 'com.android. ...

  10. React Native环境配置之Windows版本搭建

    接近年底了,回想这一年都做了啥,学习了啥,然后突然发现,这一年买了不少书,看是看了,就没有完整看完的.悲催. 然后,最近项目也不是很紧了,所以抽空学习了H5.自学啃书还是很无趣的,虽然Head Fir ...