WCF从理论到实践(7):消息交换模式

本文的出发点

通过阅读本文,您能理解以下知识:

  1. WCF定义了哪几种消息交换模式?
  2. One-Way Calls
  3. Request/Reply
  4. Duplex
  5. 用示例来解析WCF的消息交换模式

本文适合的读者

本文涉及到了SOA中的消息交换的基础概念,需要一些初级的Xml Web Service和分布式系统开发的经验,最好理解WCF架构

WCF定义了哪几种消息交换模式?

WCF定义了三种消息交换方式 ,分别为:

  1. One-Way Calls
  2. Request/Reply
  3. Duplex

One-Way Calls

在几种消息交换模式中,one-way calls是最没良心的,对于客户端,one-way calls就如肉包子打狗,有去无回。下面的图示给出这种交换模型的特征:

在这种交换模式中,存在着如下的特征

  1. 没有返回值,返回类型只能为void
  2. 不能包含ref或者out类型的参数
  3. 只有客户端发起请求,服务端并不会对请求进行回复。

通过设置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协议中的请求/响应模型。下面的图示给出这种交换模式的特征:

这种交换模式是使用最多的一中,它有如下特征:

  1. 调用服务方法后需要等待服务的消息返回,即便该方法返回 void 类型
  2. 相比Duplex来讲,这种模式强调的是客户端的被动接受,也就是说客户端接受到响应后,消息交换就结束了。
  3. 在这种模式下,服务端永远是服务端,客户端就是客户端,职责分明。

它是缺省的消息交换模式,设置OperationContract便可以设置为此种消息交换模式

[OperationContrac]

void Test(int intVal);

注意,尽管Test方法返回为void,但Server也会生成reply响应并发送给client.有来有往是这种模式的特征。

 

Duplex

这种交换模式比起上面两种,比较复杂,它和request/reply模式类似,也是有来有往,但处理过程却比request/reply要复杂,因为它可以在处理完请求之后,通过请求客户端中的回调进行响应操作,这种模式的图示为:

注意,这种方式和request/reply方式的图示也很类似,当二者存在着至关重要的不同,它在客户端也有监听节点,在callback的时候,服务器和客户端的角色会进行交换,服务端此时成了严格意义上的客户端,而客户端此时能接受服务端的callback请求,所以成为了服务端。呵呵,辩证法,都拗口死了,当事实就是这种,就像对与错一样,会相互转换,失败是成功之母,而成功是失败之源。废话少说,Duplex的特征主要包括

  1. 消息交换过程中,服务端和客户端角色会发生调换
  2. 服务端处理完请求后,返回给客户端的不是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;
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;
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;
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的代码如下:

而客户端的配置文件,如下:

<?xml version="1.0" encoding="utf-8" ?>
<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,然后运行客户端

会产生如下的结果:

服务端运行解图

客户端运行解图:

本文参考资料 

  1. http://msdn.microsoft.com/msdnmag/issues/06/10/wcfessentials/default.aspx
  2. http://www.rainsts.net/article.asp?id=428

 

本文相关示例文件

示例项目:/Files/jillzhang/Jillzhang.Messaging.rar

posted @ 2008-02-17 20:37  Robin Zhang  阅读(16485)  评论(43编辑  收藏  举报