`
varsoft
  • 浏览: 2506102 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

[原创]WCF后续之旅(16): 消息是如何分发到Endpoint的--消息筛选(Message Filter)

阅读更多

在介绍终结点的ListenUriMode时,我们提到了两个特殊的对象ChannelDispatcher和ChannelListener。这两个对象在整个WCF的消息分发系统中具有重要的地位,在这节里,我们对WCF的整个消息分发过程作一个简单的介绍。

一、连接请求的监听

当我们通过ServiceHost对某个服务进行寄宿的时候,实际上WCF是在为我们创建一个监听器,并监听来自外界的服务访问请求。我们举一个例子,比如针对服务CalculateService,具有如下的配置:该服务具有基于BasicHttpBinding的三个终结点,他们的地址(逻辑地址)分别为:http://127.0.0.1:9999/calculateservice,http://127.0.0.1:8888/calculateservicehttp://127.0.0.1:7777/calculateservice,而前两个共享同一个ListenUri——http://127.0.0.1:6666/calculateservice。而第三个使用默认的ListenUri(也就是终结点地址)。

   1: <?xml version="1.0" encoding="utf-8" ?>
<!--CRLF-->
   2: <configuration>    
<!--CRLF-->
   3:     <system.serviceModel>
<!--CRLF-->
   4:         <services>
<!--CRLF-->
   5:             <service name="Artech.WcfServices.Services.CalculateService">
<!--CRLF-->
   6:                 <endpoint address="http://127.0.0.1:9999/calculateservice" binding="basicHttpBinding"
<!--CRLF-->
   7:                     contract="Artech.WcfServices.Contracts.ICalculate" listenUri="http://127.0.0.1:6666/calculateservice" />
<!--CRLF-->
   8:                 <endpoint address="http://127.0.0.1:8888/calculateservice" binding="basicHttpBinding"
<!--CRLF-->
   9:                     contract="Artech.WcfServices.Contracts.ICalculate" listenUri="http://127.0.0.1:6666/calculateservice" />
<!--CRLF-->
  10:                 <endpoint address="http://127.0.0.1:7777/calculateservice" binding="basicHttpBinding"
<!--CRLF-->
  11:                     contract="Artech.WcfServices.Contracts.ICalculate" />
<!--CRLF-->
  12:             </service>
<!--CRLF-->
  13:         </services>
<!--CRLF-->
  14:     </system.serviceModel>
<!--CRLF-->
  15: </configuration> 
<!--CRLF-->
  16: 
<!--CRLF-->

当我们通过ServiceHost对该服务进行寄宿的时候,会为该服务创建一个ServiceHost对象。当我们执行ServiceHost的Open方法的时候,WCF会创建两个ChannelDispatcher对象。为什么会是两个ChannelDispatcher对象呢?这是因为ChannelDispatcher是根据实际的监听地址创建的,在本例中,虽然我们为服务创建了三个终结点,由于前两个共享同一个监听地址,所所以针对于服务的ServiceHost对象,具有两个ChannelDispatcher对象与之匹配。对于每个ChannelDispatcher对象而言,他们各自对应一个唯一的ChannelListener对象,ChannelListener具有两个方面的作用,其一是绑定到一个具体的URI,监听来自外界的连接请求,其二就是当检测到请求后,创建信道堆栈(channel stack)接受、处理请求。ServiceHost的Open方法的执行,同时也预示着ChannelListener监听工作的开始。

由于我们为该服务注册了三个终结点,WCF还会创建3个EndpointDispatcher对象,分别于三个终结点对应。对于服务访问请求的消息,会先被对应的ChannelDispacher(这取决于该消息是从哪个ChannelListener接收到的)接收,ChannelDispacher本身并不会对该消息进行处理,而是为将它转发到对应的EndpointDispatcher上,基于该消息的所有后续处理都叫由EndpointDispatcher进行处理。对于这三个EndpointDispatcher对象,前面两个和第一个ChannelDispatcher匹配(根据实际的监听地址进行匹配)。

总结一下,一个CalculateService服务,对应着一个ServiceHost对象。该ServiceHost对象有具有两个ChannelDispatcher对象,这两个ChannelDispatcher各自具有一个ChannelListener对象,他们对应的监听地址分别为http://127.0.0.1:6666/calculateservicehttp://127.0.0.1:7777/calculateservice。对于前一个ChannelDispatcher,具有两个与之匹配的EndpointDispatcher对象,后一个具有一个匹配的EndpointDispatcher对象。具体关系如下图所示:

image

我们可以通过一个例子在正式这一点,完全针对我们上面提供的配置,我们通过下面的代码将该服务在一个控制台应用中进行寄宿,然后打印出ChannelDispatcher和EndpointDispatcher的关系:

   1: //---------------------------------------------------------------
<!--CRLF-->
   2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
<!--CRLF-->
   3: //---------------------------------------------------------------
<!--CRLF-->
   4: using System.ServiceModel;
<!--CRLF-->
   5: using Artech.WcfServices.Services;
<!--CRLF-->
   6: using System.Threading;
<!--CRLF-->
   7: using System;
<!--CRLF-->
   8: using System.ServiceModel.Dispatcher; 
<!--CRLF-->
   9: 
<!--CRLF-->
  10: namespace Artech.WcfServices.Hosting
<!--CRLF-->
  11: {
<!--CRLF-->
  12:     class Program
<!--CRLF-->
  13:     {
<!--CRLF-->
  14:         static void Main(string[] args)
<!--CRLF-->
  15:         {
<!--CRLF-->
  16:             using (ServiceHost serviceHost = new ServiceHost(typeof(CalculateService)))
<!--CRLF-->
  17:             {
<!--CRLF-->
  18:                 serviceHost.Open();
<!--CRLF-->
  19:                 int i=0; 
<!--CRLF-->
  20: 
<!--CRLF-->
  21:                 foreach (ChannelDispatcher channelDispatcher in serviceHost.ChannelDispatchers)
<!--CRLF-->
  22:                 {
<!--CRLF-->
  23:                     Console.WriteLine("ChannelDispatcher {0}: ListenUri: {1}", ++i, channelDispatcher.Listener.Uri);
<!--CRLF-->
  24:                     int j = 0;
<!--CRLF-->
  25:                     foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
<!--CRLF-->
  26:                     {
<!--CRLF-->
  27:                         Console.WriteLine("\tEndpointDispatcher {0}: EndpointAddress: {1}", ++j,endpointDispatcher.EndpointAddress.Uri);
<!--CRLF-->
  28:                     }
<!--CRLF-->
  29:                 }
<!--CRLF-->
  30:                 Console.Read();
<!--CRLF-->
  31:             }
<!--CRLF-->
  32:         }
<!--CRLF-->
  33:     }
<!--CRLF-->
  34: } 
<!--CRLF-->
  35: 
<!--CRLF-->

最终输出的结果印证了我们上面对ServiceHost、ChannelDispatcher、ChannelListener和EndpointDispatcher的关系:

   1: ChannelDispatcher 1: ListenUri: http://127.0.0.1:6666/calculateservice
<!--CRLF-->
   2:       EndpointDispatcher 1: EndpointAddress: http://127.0.0.1:9999/calculateservice
<!--CRLF-->
   3:       EndpointDispatcher 2: EndpointAddress: http://127.0.0.1:8888/calculateservice
<!--CRLF-->
   4: ChannelDispatcher 2: ListenUri: http://127.0.0.1:7777/calculateservice
<!--CRLF-->
   5:       EndpointDispatcher 1: EndpointAddress: http://127.0.0.1:7777/calculateservice
<!--CRLF-->
2、EndpointDispatcher的选择和消息的分发

接着上面的例子,当服务被成功寄宿之后,两个ChannelDispatcher的ChannelListener便开始在各自的监听URI上进行监听。一旦某个服务调用请求被某个ChannelListener监测到,ChannelListner会调用AcceptChannel方法创建信道栈(channel stack)接收和处理请求消息。

当消息被接收信道栈处理完毕之后,ChannelListener所在的ChannelDispatcher需要将消息分发给对应的EndpointDispatcher。但是对于一个ChannelDiaptcher对应多个EndpointDispatcher的情况,究竟该如何选择适合的EndpointDispatcher呢?EndpointDispatcher的选择依赖于两个特殊的MessageFilter——AddressFilter和ContractFilter。

   1: //---------------------------------------------------------------
<!--CRLF-->
   2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
<!--CRLF-->
   3: //---------------------------------------------------------------
<!--CRLF-->
   4: public class EndpointDispatcher
<!--CRLF-->
   5: {
<!--CRLF-->
   6:     //... ...
<!--CRLF-->
   7:     public MessageFilter AddressFilter { get; set; }
<!--CRLF-->
   8:     public MessageFilter ContractFilter { get; set; }
<!--CRLF-->
   9: } 
<!--CRLF-->
  10: 
<!--CRLF-->

我们先来看看MessageFilter的定义, 如下所示,MessageFilter类定义两个重载的Match方法,参数分别是Message和MessageBuffer。当MessageFilter的Match方法返回True,就表明该MessageFilter对象对应的EndpointDispatcher正是真正被请求的EndpointDispatcher。也就是说当ChannelDispatcher进行筛选的时候,会遍历它所有的EndpointDispatcher,获取他们的AddressFilter和ContractFilter,调用Match方法,如果两者都返回true,则表明是真正的需要的EndpointDispatcher。

   1: //---------------------------------------------------------------
<!--CRLF-->
   2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
<!--CRLF-->
   3: //---------------------------------------------------------------
<!--CRLF-->
   4: public abstract class MessageFilter
<!--CRLF-->
   5: {
<!--CRLF-->
   6:     public abstract bool Match(Message message);
<!--CRLF-->
   7:     public abstract bool Match(MessageBuffer buffer);
<!--CRLF-->
   8: } 
<!--CRLF-->
   9: 
<!--CRLF-->

WCF定义了6种MessageFilter:ActionMessageFilter、EndpointAddressMessageFilter、XPathMessageFilter、PrefixEndpointAddressMessageFilter、MatchAllMessageFilter和MatchNoneMessageFilter。如下面的类图所示,这6种MessageFilter均继承自抽象类:System.ServiceModel.Dispatcher.MessageFilter。

  • ActionMessageFilter:对于服务契约的每个操作都具有一个Action,可以是显示指定的,也可以是默认的(服务契约的命名空间+操作名称),也就是说一个终结点的具有一个Action列表。在进行筛选的时候,如果SOAP消息的Action报头的值存在于终结点的Action列表中,则匹配成功
  • EndpointAddressMessageFilter:如果SOAP消息的To报头和终结点的地址完全一样,则匹配成功
  • XPathMessageFilter:SOAP消息也是一个XML,所以可以根据一个具体的XPath表达式和SOAP的内容进行匹配
  • PrefixEndpointAddressMessageFilter:和EndpointAddressMessageFilter一样,也是通过SOAP消息的To报头和终结点的地址进行比较,不过这里仅仅比较地址的前缀
  • MatchAllMessageFilter:不管消息的内容是什么,都会匹配成功
  • MatchNoneMessageFilter:和MatchAllMessageFilter相反,不管消息的内容是什么,都不会匹配成功

MessageFilter

在默认的情况下,EndpointDispatcher的AddressFilter采用的是EndpointAddressMessageFilter,而ContractFilter采用的是ActionMessageFilter。如果希望改变EndpointDispatcher的AddressFilter和ContractFilter的值,你可以通过自定义Behavior的形式覆盖掉默认的值。对于AddressFilter,你有一种最直接的方式,通过ServiceBehaviorAttribute的AddressFilterMode属性指定你所需要的MessageFilter。

   1: //---------------------------------------------------------------
<!--CRLF-->
   2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
<!--CRLF-->
   3: //---------------------------------------------------------------
<!--CRLF-->
   4: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
<!--CRLF-->
   5: {
<!--CRLF-->
   6:     //... ...
<!--CRLF-->
   7:     public AddressFilterMode AddressFilterMode { get; set; }
<!--CRLF-->
   8: }
<!--CRLF-->
   9: public enum AddressFilterMode
<!--CRLF-->
  10: {
<!--CRLF-->
  11:     Exact,
<!--CRLF-->
  12:     Prefix,
<!--CRLF-->
  13:     Any
<!--CRLF-->
  14: } 
<!--CRLF-->

其中Exact对应EndpointAddressMessageFilter;Prefix对应PrefixEndpointAddressMessageFilter;Any对应MatchAllMessageFilter。比如通过下面的代码,将AddressFilter指定为MatchAllMessageFilter:

   1: //---------------------------------------------------------------
<!--CRLF-->
borde

  


  
分享到:
评论

相关推荐

    WCF布署问题1 :HTTP 错误 404.17 - Not Found 请求的内容似乎是脚本,因而将无法由静态

    2. **ISAPI筛选器或扩展未启用**:IIS可能没有启用处理WCF服务所需的ISAPI筛选器(如ASP.NET)或扩展。 3. **MIME类型不匹配**:IIS可能没有为WCF服务的.svc文件设置正确的MIME类型。 4. **路由规则问题**:如果使用...

    消息安全身份验证 wcf

    消息安全模式之UserName客户端身份验证 制作证书: 1:Microsoft Visual Studio 2008--&gt;Visual Studio Tools--&gt;Visual Studio 2008 命令提示行。输入:makecert -r -pe -n ...

    第三节:ExtJS调用WCF系列-----添加,修改,删除 - 小庄 - 博客园

    这是一个Ext框架,我对此框架很感兴趣。。。特别想了解一下。。。

    WCF 我的WCF之旅示例代码

    在这个“WCF我的WCF之旅示例代码”中,我们可以看到一系列逐步进阶的代码示例,这些示例有助于理解WCF的核心概念和技术。 首先,我们要了解WCF的基本组成部分: 1. **服务**:服务是WCF中的核心元素,它定义了对外...

    (14):消息队列-WCF课件

    ### 消息队列-WCF课件知识点解析 #### 一、WCF与面向服务的应用程序 ##### 1.1 构建WCF面向服务的应用程序 - **定义**: Windows Communication Foundation (WCF) 是一个统一的编程模型,用于创建服务导向的应用...

    WCF学习笔记,一步一步教你学习WCF

    ### WCF学习笔记知识点概述 #### 一、基本概念 - **定义**:WCF(Windows Communication Foundation)是微软提供的一种框架,用于构建基于服务的应用程序。它将各种通信技术如HTTP、TCP/IP、MSMQ等整合到一个统一的...

    WCF之旅:一个简单的WCF程序(vs2010源码)

    **WCF之旅:一个简单的WCF程序** ...总结,WCF之旅是一个从零开始接触和学习WCF的好起点,通过实践一个简单的程序,你可以了解到服务的创建、配置、调用等基础操作,为后续深入学习WCF和其他相关技术打下坚实的基础。

    我的WCF之旅:计算器 WCF样例源代码

    我的WCF之旅- 创建一个简单的WCF程序 - Artech WCF入门之选绝佳的例子 代源源于:《WCF全面解析 上》 编程工具:VS2010 语言:C# blog 《IIS站点中部署 WCF项目》

    WCF分布式开发技术讲座

    提供的"ReadMe.txt"文件可能包含了课程大纲、讲师介绍和学习指导,而"WCF分布式开发技术讲座_1_从.NET到WCF:WCF优势、学习方法、资源和路线图(免费试听30分钟).wmv"视频文件则是一个入门引导,涵盖了WCF的优势、...

    我的WCF之旅源代码 IIS寄宿

    **WCF之旅源代码——IIS寄宿与泛型序列化** Windows Communication Foundation(WCF)是微软.NET框架中用于构建分布式应用程序的服务模型。在这个源代码包中,我们聚焦于两个核心概念:IIS(Internet Information ...

    (7):消息模式-WCF课件

    发布与订阅是一种消息传递模式,其中消息发布者发送消息到一个中心中介(通常是消息代理),订阅者可以注册感兴趣的消息类型,并从中介接收这些消息。这种模式广泛应用于分布式系统中,特别是在需要一对多或多对多...

    构建WCF面向服务的应用程序系列课程(7):消息模式.zip

    在本课程中,我们将深入探讨Windows Communication Foundation (WCF) 中的消息模式,这是构建面向服务应用程序的关键组成部分。WCF是微软提供的一种强大的框架,用于创建分布式、安全、可靠的跨平台服务。消息模式在...

    我的WCF之旅源代码_创建一个简单的WCF程序

    **WCF(Windows Communication Foundation)**是微软.NET框架下的一个组件,用于构建、配置和部署服务。它提供了统一的编程模型,支持多种通信协议和数据格式,如HTTP、TCP、MSMQ等,使得服务可以跨平台、跨网络进行...

    WCF中动态设置KnownType的示例代码

    动态配置Known Types,解决WCF动态扩展属性类型的问题: Unhandled Exception: System.Runtime.Serialization.SerializationException: Type '--------' with data contract name 'Dog:...

    WCF消息拦截示例代码

    **WCF消息拦截示例代码**是针对Windows Communication Foundation(WCF)服务的一种技术实现,它允许我们在消息发送和接收时进行自定义处理。在WCF中,这种功能通常是通过实现`IDispatchMessageInspector`(服务端)...

    WCF中动态设置KnownType的示例

    动态配置Known Types,解决WCF动态扩展属性类型的问题: Unhandled Exception: System.Runtime.Serialization.SerializationException: Type '--------' with data contract name 'Dog:...

    WCF-Message-Security:使用证书的WCF消息安全性

    **消息安全性(Message Security)**模式强调在传输层之上提供安全保护,它不仅确保数据的安全传输,还能处理身份验证和授权。 ### 2. 消息安全性与证书 在WCF中,**使用证书**是实现消息安全性的常见方式。证书...

    我的WCF之旅后续篇

    我的WCF之旅后续篇,呀,要大于20字符啊,废话一下

Global site tag (gtag.js) - Google Analytics