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所示:
图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所示:
图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所示:
图3
这里tcpTrace监听的端口号就是我们在客户端配置的物理地址端口号,而分发地址才是服务的真正地址,最终可以看到截获的消息,如图4所示:
图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所示:
图5
可以看到,利用物理地址和逻辑地址的知识,可以轻松的实现路由,当然tcpTrace只是路由中非常简单的一种使用,后面我们还会讲到更加复杂的应用。
结束语
本文详细介绍了WCF中的物理地址和逻辑地址,它的相关原理以及如何使用tcpTrace来实现SOAP消息的跟踪。关于WCF寻址相关文章:
WCF专题系列(4):深入WCF寻址Part 4—自定义消息筛选器
WCF专题系列(3):深入WCF寻址Part 3—消息过滤引擎
Worktile,新一代简单好用、体验极致的团队协同、项目管理工具,让你和你的团队随时随地一起工作。完全免费,现在就去了解一下吧。
https://worktile.com