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

作者:jillzhang
出处:http://jillzhang.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
标签: WCF, 消息交换
posted @ 2008-02-17 20:37 Robin Zhang 阅读(11382) 评论(36) 编辑 收藏

 回复 引用 查看   
#1楼[楼主] 2008-02-17 20:55 jillzhang      
用Duplex可以实现Event,将在下文中学习
 回复 引用 查看   
#2楼 2008-02-17 21:01 Tristan(Guozhijian)      
呵呵,这个比喻有趣
 回复 引用 查看   
#3楼 2008-02-17 21:01 fox23      
赞~

BTW:图片很黄很暴力的说。。。

学习了,不错,希望博主继续
 回复 引用 查看   
#5楼[楼主] 2008-02-18 08:41 jillzhang      
@Tristan(Guozhijian)
@fox23
@努力成为民工
多谢支持

 回复 引用   
#6楼 2008-02-18 09:41 dxd[未注册用户]
学习中。。。
 回复 引用 查看   
#7楼 2008-02-18 13:01 BlueMountain      
多谢 学习
 回复 引用 查看   
#8楼 2008-02-18 16:34 A.Z      
remoting...
 回复 引用 查看   
#9楼[楼主] 2008-02-19 20:56 jillzhang      
@A.Z
和Remoting相比,CallBackContract的出现使得WCF实现双向通讯要简单的多

 回复 引用 查看   
#10楼 2008-02-21 14:58 IamV      
通读一遍!受益匪浅!
多谢!

 回复 引用   
#11楼 2008-03-11 09:32 lady[未注册用户]
讲的太好了
 回复 引用   
#12楼 2008-04-15 18:23 年[未注册用户]
问一下,ICallBack也要[ServiceContract]属性修饰吗?我去掉了也能正常运行,这个是为什么呢?
 回复 引用 查看   
#13楼 2008-04-21 14:37 BAsil      
顶一下
 回复 引用   
#14楼 2008-05-15 17:00 YJH[未注册用户]
--引用--------------------------------------------------

--------------------------------------------------------
WCF是这样的,回调契约默认就是被标记ServiceContract的,所以你加不加都一个样的,当然里面的方法还是要加上OperatorContract的

 回复 引用   
#15楼 2008-05-30 21:00 织诗_恒[未注册用户]
好~~~
 回复 引用 查看   
#16楼 2008-06-10 19:12 flank.chen      
还行
 回复 引用 查看   
#17楼 2008-07-08 11:54 不若相忘于江湖      
楼主很牛。


 回复 引用   
#18楼 2008-08-27 09:11 sharezoon[未注册用户]
博主没介绍客户端实现的ICallback---MyCallback这个类啊
 回复 引用 查看   
#19楼[楼主] 2008-08-27 09:32 jillzhang      
@sharezoon
MyCallback类是对ICallback的实现,在实例项目中有。

 回复 引用   
#20楼 2008-09-10 23:43 Joanna[未注册用户]
真的非常感谢!您的文章中的图的注解非常好!特别对于我这种初学者来说真是受益非浅!很喜欢!
 回复 引用 查看   
#21楼[楼主] 2008-09-11 11:59 jillzhang      
@Joanna
多谢支持

 回复 引用 查看   
#22楼 2008-10-10 23:42 品味从容      
楼主你好:小弟是个菜鸟,比着你的代码从头到尾抄写了一遍,最后发现不会生成Client端的代理cs文件。麻烦你指点下,谢谢。
 回复 引用 查看   
#23楼 2008-10-10 23:54 品味从容      
哦,还有一点,一个解决方案下,怎么同时启动2个Console程序???
 回复 引用 查看   
#24楼 2008-10-12 11:11 守護~︶箬      
--引用--------------------------------------------------
品味从容: 哦,还有一点,一个解决方案下,怎么同时启动2个Console程序???
--------------------------------------------------------
在项目中右键添加服务应用。跟Webservice是一个道理。然后会自动生成客户端代理类。

 回复 引用 查看   
#25楼 2008-10-12 11:12 守護~︶箬      
--引用--------------------------------------------------
品味从容: 哦,还有一点,一个解决方案下,怎么同时启动2个Console程序???
--------------------------------------------------------
建了好多项目,在每个项目下右键 调试 就ok。(对Console程序而言)

 回复 引用   
#26楼 2008-10-25 22:52 王明星[未注册用户]
这三种消息交换模式不外呼就是计算机网络里面提到的单工,双工,半双工吧。
 回复 引用 查看   
#27楼 2010-01-12 14:47 梅桦      
@Tristan(Guozhijian)
那返回值是不是就是搓下来的泥?

 回复 引用 查看   
#28楼 2010-06-18 17:58 一扬      
Robin,你好, 请教一个问题,我的WCF服务层向客服端传送一个非常大的对象,该对象封装了38万多条数据,我已经设置 maxItemsInObjectGraph为最大了
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
但还是报了以下错误
System.ServiceModel.CommunicationException: 接收对 http://192.168.1.199:7777/gisService 的 HTTP 响应时发生错误。这可能是由于服务终结点绑定未使用 HTTP 协议造成的。这还可能是由于服务器中止了 HTTP 请求上下文(可能由于服务关闭)所致。有关详细信息,请参阅服务器日志。 ---> System.Net.WebException: 基础连接已经关闭: 接收时发生错误。 ---> System.IO.IOException: 无法从传输连接中读取数据: 远程主机强迫关闭了一个现有的连接。
请问WCF如何解决这种大数据量传输的问题呢?

 回复 引用 查看   
#29楼 2010-09-01 17:46 迎风而驰      
楼主太强大了!两年前就有如此深厚的造诣。现在应该是出神入化了吧。不知道还会不会解答我们这些小菜的疑惑呢!
回到正题上,我想请问一下,如果用WCF建立多媒体的流转发服务,不知现实不?而且要效果还不错的。这个跟28楼的好像有点相似,也是大数据量的传输问题,不知道我有机会得到楼主的指点不?

 回复 引用 查看   
#30楼 2010-09-14 14:46 破-恶      
楼主 强大
请问 Client 里面 的几个Proxy 请问有什么作用呢??
小弟初学 ~~

 回复 引用 查看   
#31楼 2010-11-22 15:50 草珊瑚      
学习了,您真是走在时代的潮流
 回复 引用 查看   
#32楼 2010-12-02 17:54 五子登科      
@品味从容
@守護~︶箬
有两种方式:1、右键添加服务引用。2、通过微软的工具svcutil.exe生成代理类。楼主是通过第二种方式实现的

 回复 引用 查看   
#33楼 2011-03-11 17:27 garfieldcyg      
张老师, 那种彩色示意图是用什么工具画的?
 回复 引用 查看   
#34楼 2011-03-27 20:22 牧马      
张老师,请问我用vs2008写配置文件,提示没有那个identity 的元素,不知道该怎么写了。。还请老师解惑