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. Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数

    上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...

  2. NodeJs之Path

    Path模块 NodeJs提供的Path模块,使得我们可以对文件路径进行简单的操作. API var path = require('path'); var path_str = '\\Users\\ ...

  3. 使用Monit监控本地进程

    目前用它监控某些服务,失败自动重启,同时监控特定的日志文件,如果有变化,就发邮件报警 安装不细写了,网上好多 我先用cat /proc/version看了下我的系统是el6的,于是wget http: ...

  4. 【大型网站技术实践】初级篇:借助LVS+Keepalived实现负载均衡

    一.负载均衡:必不可少的基础手段 1.1 找更多的牛来拉车吧 当前大多数的互联网系统都使用了服务器集群技术,集群即将相同服务部署在多台服务器上构成一个集群整体对外提供服务,这些集群可以是Web应用服务 ...

  5. HTML 事件(三) 事件流与事件委托

    本篇主要介绍HTML DOM中的事件流和事件委托. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三) 事件流与事件委托 4 ...

  6. Linux下Nodejs安装(完整详细)

    之前安装过windows下以及Mac下的node,感觉还是很方便的,不成想今天安装linux下的坑了老半天,特此记录. 首先去官网下载代码,这里一定要注意安装分两种,一种是Source Code源码, ...

  7. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  8. iOS开发之Alamofire源码深度解析

    今天博客中的Alamofire源码的版本是以现在最新的3.4版本为例.上篇博客系统的对NSURLSession相关的东西进行了详细的解析,详情请看<详解NSURLSession>,为了就是 ...

  9. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  10. 动手做第一个Chrome插件

    Chrome插件是令人惊讶的简单,一旦你弄懂它的工作和实现原理.它是由一部分HTML,一部分Js,然后混合了一个叫做manifest.json的Json文件组合而成的整体.这意味着你可以使用你最擅长的 ...