WCF后传系列(5):深入WCF寻址Part 5—逻辑地址和物理地址

概述

在WCF中,每个服务终结点都与两个地址相关联,一个逻辑地址和一个物理地址,逻辑地址就是SOAP消息的目标地址,即前面不止一次提到的“To”地址,而物理地址是WCF侦听器真正监听的地址。在WCF中,逻辑地址称之为终结点地址Endpoint Address,而物理地址则称之为监听地址ListenUri。

本文将详细介绍WCF中的物理地址和逻辑地址,以及如何使用tcpTrace来进行SOAP消息的跟踪。

两种地址

WCF中,物理地址负责使用特定的传输协议在特定的位置接收传入的消息,除非特别指定,否则逻辑地址将被用来做物理地址,换句话说,在以前我们对于终结点所配置的EndpointAddress都是指定了逻辑地址,如我们的服务端配置如下:

<endpoint address="http://localhost:8887/CalculatorService1"
          binding ="wsHttpBinding"
          contract="TerryLee.WCFAddressing.Contract.ICalculator">
</endpoint>
<endpoint address="http://localhost:8887/CalculatorService2"
          binding ="basicHttpBinding"
          contract="TerryLee.WCFAddressing.Contract.ICalculator">
</endpoint>

现在我们输出一下,就可以看到两个地址是同样的值,如下代码所示:

using (ServiceHost calculatorServiceHost =
        new ServiceHost(typeof(CalculatorService)))
{
    calculatorServiceHost.Opened += delegate
    {
        Console.WriteLine("Service begin to listen via the Address:{0}",
            calculatorServiceHost.BaseAddresses[0].ToString());
    };

    calculatorServiceHost.Open();

    foreach (ServiceEndpoint se in calculatorServiceHost.Description.Endpoints)
    {
        Console.WriteLine("Endpoint details:");
        Console.WriteLine("Logical address: \t{0}", se.Address);
        Console.WriteLine("Physical address: \t{0}", se.ListenUri);
        Console.WriteLine("Binding: \t{0}", se.Binding.Name);
        Console.WriteLine();
    }
    Console.Read();
}

输出结果如图1所示:

TerryLee_WCF_13

图1

设定物理地址

前面我们输出的结果逻辑地址和物理地址是相同的,我们可以通过代码或者配置文件来设定终结点的物理地址。

WSHttpBinding wsbinding = new WSHttpBinding();

calculatorServiceHost.AddServiceEndpoint(
    typeof(ICalculator),
    wsbinding,
    "urn:calcservice",  // 逻辑地址
    new Uri("http://localhost:8887/CalculatorService")  // 物理地址
);

又或者通过配置文件来设置ListenUri,如下代码所示:

<endpoint address="urn:calcservice"
          binding ="wsHttpBinding"
          contract="TerryLee.WCFAddressing.Contract.ICalculator"
          listenUri="http://localhost:8887/CalculatorService"
          bindingConfiguration="securityBinding">
</endpoint>

这里我们只需要注意的是在指定物理地址时,仍然可以使用相对地址,这一点与设置逻辑地址时是一样的。

工作原理

现在思考一个核心的问题,当我们定义了终结点后,在WSDL中包含的是每个终结点的逻辑地址,而非物理地址,如下代码片段:

<wsdl:service name="CalculatorService">
  <wsdl:port name="WSHttpBinding_ICalculator" binding="tns:WSHttpBinding_ICalculator">
    <soap12:address location="urn:calcservice" />
    <wsa10:EndpointReference>
      <wsa10:Address>urn:calcservice</wsa10:Address>
    </wsa10:EndpointReference>
  </wsdl:port>
</wsdl:service>

如果物理地址与逻辑地址相同的,就不会有任何问题,但是客户端如何与一个配置了不同物理地址的服务进行交互?因为客户端并不关心服务端是否配置了不同的物理地址,它只知道每个终结点有一个唯一的终结点地址,只需要跟该地址交互即可,该地址也将作为SOAP消息放在“To”标头中。

这时我们需要有一个特殊机制,来通知客户端要使用的物理地址,然后客户端通过物理地址传送外发消息,就如同它是路由器或者某种类型的中介一样,可以通过ClientViaBehavior来实现这一点,如下代码所示:

<system.serviceModel>
  <client>
    <endpoint address="urn:calcservice"
              binding="wsHttpBinding"
              contract="TerryLee.WCFAddressing.Contract.ICalculator"
              name="defualtEndpoint"
              behaviorConfiguration="calculatorEndpointBehavior"
              bindingConfiguration="securityBinding">
    </endpoint>
  </client>
  <behaviors>
    <endpointBehaviors>
      <behavior name="calculatorEndpointBehavior">
        <clientVia viaUri="http://localhost:8887/CalculatorService" />
      </behavior>
    </endpointBehaviors>
  </behaviors>
</system.serviceModel>

此时客户端将通过与服务终结点相同的物理地址(http://localhost:8887/CalculatorService)向外传送消息而不是通过“urn:calcservice”,但请注意,在SOAP消息“To”标头中包含的仍然是逻辑地址,如图2所示:

TerryLee_WCF_14

图2

看到上面这幅图,可能大家还有一个疑问,逻辑地址起什么作用呢?别忘了我们前面讲到的消息筛选,当消息到达时,ChannelDispatcher 查询每个相关的 EndpointDispatcher 对象以确定终结点是否可以接受消息,以及将该消息传递到可以接受消息的终结点。当消息的目标地址(To标头中的地址)与 AddressFilter 属性相匹配并且消息操作与 ContractFilter 属性相匹配时,EndpointDispatcher 对象负责处理来自 ChannelDispatcher 的消息。

物理地址模式

了解了物理地址和逻辑地址之间的关系,我们再看一下在设置监听地址时的两种模式,通过ListenUriMode枚举来指定,它定义了两个枚举值:

Explicit:完全原样使用 ListenUri,默认值。

Unique:指定传输是否应使用特定传输机制,以确保 ListenUri 是唯一的

根据传输所采用的协议不同,WCF会采用不同的策略来保证ListenUri唯一,具体的策略如下所示:

1.非TCP传输,在ListenUri的末尾附加一个GUID。

2.对于独占模式下的 TCP(PortSharingEnabled 为 false),绑定到一个唯一可用端口号。

3.对于端口共享模式下的 TCP(PortSharingEnabled 为 true),在ListenUri的末尾附加一个GUID。

TcpTrace消息截获

前面讲了这么多物理地址和逻辑地址,它们最重要的使用地方就是做路由。我们常用tcpTrace来做SOAP消息跟踪,它正是利用这一点技术,在客户端配置ClientViaBehavior,指向tcpTrace的侦听地址,然后tcpTrace在对消息做记录后再转发到服务端,如在服务端的配置如下,它的物理地址和逻辑是相同的:

<service name="TerryLee.WCFAddressing.Service.CalculatorService"
         behaviorConfiguration="calculatorBehavior">
  <host>
    <baseAddresses>
      <add baseAddress="http://localhost:8887/Calculator"/>
    </baseAddresses>
  </host>
  <endpoint address="http://localhost:8887/CalculatorService"
            binding ="wsHttpBinding"
            contract="TerryLee.WCFAddressing.Contract.ICalculator">
  </endpoint>
</service>

客户端的配置,这里“http://localhost:8887/CalculatorService”是真正的服务地址(逻辑地址),我们通过ClientViaBehavior告诉客户端物理地址是“http://localhost:8080/CalculatorService”,事实上处于该物理地址的服务并不存在,该地址是tcpTrace的监听地址:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <client>
      <endpoint address="http://localhost:8887/CalculatorService"
                binding="wsHttpBinding"
                contract="TerryLee.WCFAddressing.Contract.ICalculator"
                behaviorConfiguration="calculatorEndpointBehavior">
      </endpoint>
    </client>
    <behaviors>
      <endpointBehaviors>
        <behavior name="calculatorEndpointBehavior">
          <clientVia viaUri="http://localhost:8080/CalculatorService" />
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

tcpTrace的配置如图3所示:

TerryLee_WCF_16

图3

这里tcpTrace监听的端口号就是我们在客户端配置的物理地址端口号,而分发地址才是服务的真正地址,最终可以看到截获的消息,如图4所示:

TerryLee_WCF_17

图4

如果不在客户端配置ClientViaBehavior,利用物理地址和逻辑地址的知识,我们还可以有另外一种方式使用tcpTrace。前面我说过,逻辑地址是包含在WSDL中,所以对于客户端来说知道的是逻辑地址,它会向该地址发送消息,这样我们可以配置终结点的逻辑地址为tcpTrace侦听的地址,而为服务端指定另外一个物理地址,并配置tcpTrace向该物理地址转发消息,如服务端的配置如下:

<service name="TerryLee.WCFAddressing.Service.CalculatorService"
         behaviorConfiguration="calculatorBehavior">
  <host>
    <baseAddresses>
      <add baseAddress="http://localhost:8080/Calculator"/>
    </baseAddresses>
  </host>
  <endpoint address="http://localhost:8887/CalculatorService"
            binding ="wsHttpBinding"
            contract="TerryLee.WCFAddressing.Contract.ICalculator"
            listenUri="http://localhost:8080/CalculatorService">
  </endpoint>
</service>

而客户端则不用再配置ClientViaBehavior,如下代码所示:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <client>
      <endpoint address="http://localhost:8887/CalculatorService"
                binding="wsHttpBinding"
                contract="TerryLee.WCFAddressing.Contract.ICalculator">
      </endpoint>
    </client>
  </system.serviceModel>
</configuration>

现在“http://localhost:8887/CalculatorService”是逻辑地址,配置tcpTrace监听该地址,并向服务的物理地址“http://localhost:8080/CalculatorService”转发消息,如图5所示:

TerryLee_WCF_18

图5

可以看到,利用物理地址和逻辑地址的知识,可以轻松的实现路由,当然tcpTrace只是路由中非常简单的一种使用,后面我们还会讲到更加复杂的应用。

结束语

本文详细介绍了WCF中的物理地址和逻辑地址,它的相关原理以及如何使用tcpTrace来实现SOAP消息的跟踪。关于WCF寻址相关文章:

WCF专题系列(4):深入WCF寻址Part 4—自定义消息筛选器

WCF专题系列(3):深入WCF寻址Part 3—消息过滤引擎

WCF专题系列(2):深入WCF寻址Part 2—自定义寻址报头

WCF专题系列(1):深入WCF寻址Part 1—Web服务寻址规范

posted @ 2008-10-31 00:16  TerryLee  阅读(9263)  评论(12编辑  收藏  举报