WCF从理论到实践(7):消息交换模式
系列文章导航:
WCF从理论到实践(16):操作重载(带视频+ppt+源码)
WCF从理论到实践(17):OO大背离(带视频+ppt+源码)
本文的出发点
通过阅读本文,您能理解以下知识:
- WCF定义了哪几种消息交换模式?
- One-Way Calls
- Request/Reply
- Duplex
- 用示例来解析WCF的消息交换模式
本文适合的读者
本文涉及到了SOA中的消息交换的基础概念,需要一些初级的Xml Web Service和分布式系统开发的经验,最好理解WCF架构
WCF定义了哪几种消息交换模式?
WCF定义了三种消息交换方式 ,分别为:
- One-Way Calls
- Request/Reply
- Duplex
One-Way Calls
在几种消息交换模式中,one-way calls是最没良心的,对于客户端,one-way calls就如肉包子打狗,有去无回。下面的图示给出这种交换模型的特征:
在这种交换模式中,存在着如下的特征
- 没有返回值,返回类型只能为void
- 不能包含ref或者out类型的参数
- 只有客户端发起请求,服务端并不会对请求进行回复。
通过设置OperationContract的IsOneWay=True可以将满足要求的方法设置为这种消息交换模式,方法如下:
[OperationContract(IsOneWay=true)]
void Test(int intVal);
上面的代码,就是将方法Test设置成为了one-way call的消息交换模式,注意如果Test方法的返回类型不是void或者带有ref或者out类型的参数,都会抛出异常InvalidOperationException,如下面列表中的方法均不能被声明为one-way模式
int Test(int intVal);
int Test();
int Test();
void Test(ref int intVal);
void Test(out int intVal);
Request/Reply
request/reply比起one-way来说,就更懂得礼尚往来,它是缺省的消息交换模式,类似于http协议中的请求/响应模型。下面的图示给出这种交换模式的特征:
这种交换模式是使用最多的一中,它有如下特征:
- 调用服务方法后需要等待服务的消息返回,即便该方法返回 void 类型
- 相比Duplex来讲,这种模式强调的是客户端的被动接受,也就是说客户端接受到响应后,消息交换就结束了。
- 在这种模式下,服务端永远是服务端,客户端就是客户端,职责分明。
它是缺省的消息交换模式,设置OperationContract便可以设置为此种消息交换模式
[OperationContrac]
void Test(int intVal);
注意,尽管Test方法返回为void,但Server也会生成reply响应并发送给client.有来有往是这种模式的特征。
Duplex
这种交换模式比起上面两种,比较复杂,它和request/reply模式类似,也是有来有往,但处理过程却比request/reply要复杂,因为它可以在处理完请求之后,通过请求客户端中的回调进行响应操作,这种模式的图示为:
注意,这种方式和request/reply方式的图示也很类似,当二者存在着至关重要的不同,它在客户端也有监听节点,在callback的时候,服务器和客户端的角色会进行交换,服务端此时成了严格意义上的客户端,而客户端此时能接受服务端的callback请求,所以成为了服务端。呵呵,辩证法,都拗口死了,当事实就是这种,就像对与错一样,会相互转换,失败是成功之母,而成功是失败之源。废话少说,Duplex的特征主要包括
- 消息交换过程中,服务端和客户端角色会发生调换
- 服务端处理完请求后,返回给客户端的不是reply,而是callback请求。
打个比方,Reqeust/Reply方式像是搓澡,1个管搓,1个被搓
而duplex像是拳击,两个人都会出拳
Duplex模式对Bindding有特殊的要求,它要求支持Duplex MEP(Message Exchange Pattern),如WSDualHttpBinding和NetTcpBinding,有关Binding的介绍请参见http://www.cnblogs.com/jillzhang/archive/2008/02/03/1063406.html
用示例来解析WCF的消息交换模式
建立示例的步骤不做具体阐述,下面看一下项目的最终结构:
下表说明各个项目的作用
项目名称 |
项目作用 |
包含文件 |
Jillzhang.Messaging.Contract |
定义WCF服务端和客户端共同使用的Contract接口 |
IOneWayJob.cs INormalJob.cs IJob.cs ICallback.cs |
Jillzhang.Messaging.Service |
实现WCF服务的Contract |
OneWayJob.cs NormalJob.cs Job.cs |
Jillzhang.Messaging.Host |
一个Console应用程序,用于承载WCF服务端 |
Program.cs App.config |
Jillzhang.Messaging.WebSite |
一个用于WebSite,用于承载WCF服务。是例外一中Host |
OnewayService.svc NormalJobService.svc JobService.svc web.config |
Jillzhang.Messaging.Client |
WCF客户端,一个Console应用程序 |
OnewayProxy.cs NormalJobProxy.cs DuplexProxy.cs MyCallback.cs Program.cs app.config |
下面就看下如何定义消息交换模式为one-way的Contract接口
而IOneWayJob的实现类代码为:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Jillzhang.Messaging.Contract;
namespace Jillzhang.Messaging.Service
{
public class OneWayJob : IOneWayJob
{
public void Do(string jobName)
{
System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();
watcher.Start();
System.Threading.Thread.Sleep(1000);
Console.WriteLine("服务" + AppDomain.CurrentDomain.FriendlyName + "执行任务:" + jobName);
watcher.Stop();
}
}
}
Request/reply的Contract接口定义如下:
而INormalJob的实现代码如下:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Jillzhang.Messaging.Contract;
namespace Jillzhang.Messaging.Service
{
public class NormalJob:INormalJob
{
public string Do(string jobName)
{
try
{
System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();
watcher.Start();
System.Threading.Thread.Sleep(1000);
Console.WriteLine("服务" + AppDomain.CurrentDomain.FriendlyName + "执行任务:" + jobName);
watcher.Stop();
return "成功";
}
catch
{
return "失败";
}
}
}
}
Duplex的交换模式需要现定义Callback的Contract接口,如下:
而服务端的Contract接口为:
Duplex的Contract实现为:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Jillzhang.Messaging.Contract;
using System.ServiceModel;
namespace Jillzhang.Messaging.Service
{
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)]
public class Job:IJob
{
public string Do(string jobName)
{
try
{
ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
System.Diagnostics.Stopwatch watcher = new System.Diagnostics.Stopwatch();
watcher.Start();
System.Threading.Thread.Sleep(1000);
Console.WriteLine("服务" + AppDomain.CurrentDomain.FriendlyName + "执行任务:" + jobName);
watcher.Stop();
callback.Done((int)watcher.ElapsedMilliseconds);
return "成功";
}
catch
{
return "失败";
}
}
}
}
下面,我们来看一下,如何创建承载服务的应用程序,首先在app.config做如下配置
而Host的代码如下:
而客户端的配置文件,如下:
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="netTcpBinding" />
netTcpBinding>
bindings>
<client>
<endpoint address="net.tcp://localhost:6987/Service/duplex" binding="netTcpBinding"
bindingConfiguration="netTcpBinding" contract="Jillzhang.Messaging.Contract.IJob"
name="NetTcpBinding">
<identity>
<dns value="localhost" />
identity>
endpoint>
<endpoint address="net.tcp://localhost:6987/Service/oneway" binding="netTcpBinding"
bindingConfiguration="netTcpBinding" contract="Jillzhang.Messaging.Contract.IOneWayJob"
name="NetTcpBinding">
<identity>
<dns value="localhost" />
identity>
endpoint>
<endpoint address="net.tcp://localhost:6987/Service/normal" binding="netTcpBinding"
bindingConfiguration="netTcpBinding" contract="Jillzhang.Messaging.Contract.INormalJob"
name="NetTcpBinding">
<identity>
<dns value="localhost" />
identity>
endpoint>
client>
system.serviceModel>
configuration>
需要注意的是:在设定Duplex模式时,如果服务端采用的是WsDualHttpBinding,而不是本文中的NetTcpBinding,最好指定以下clientBaseAddress,默认情况下,clientBaseAddress会尝试用80端口,可通常情况80端口都是被占用,你需要设置一个其他端口。
因为回调的Contract实现是在客户端的,所以需要在客户端实现1个ICallback实现,代码如下:
下面是客户端调用的代码:
首先运行服务承载程序Jillzhang.Messaging.Host,然后运行客户端
会产生如下的结果:
服务端运行解图
客户端运行解图:
本文参考资料
- http://msdn.microsoft.com/msdnmag/issues/06/10/wcfessentials/default.aspx
- http://www.rainsts.net/article.asp?id=428
本文相关示例文件