本文是终结点与服务寻址系列的第五篇文章。
5.分离终结点部署的物理地址和逻辑地址
在实验1中,我曾经提到过物理地址与逻辑地址的概念,其实物理地址就是服务宿主为了接收服务调用请求信息而建立的一个监听地址,而信道监听器正是负责这一工作的WCF组件。当有消息到达这个监听地址时,信道分派器(ChannelDispatcher)就会从它所关联的若干个终结点中,选出一个合适的来处理该消息,而判断一个终结点是否合适的工具,就是每个终结点的终结点分派器(EndpointDispatcher)所使用的两个筛选器,分别是地址筛选器(AddressFilter)和服务契约筛选器(ContractFilter)。它们都是MessageFilter类型,通过设置终结点分派器所使用的筛选器,就可以定制消息筛选的方式。具体如何设置和实验证明,在往后补上。现在回到本文的主题。
首先我给出服务宿主程序的代码,这些代码修改成可以显示更多的信息:
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
foreach (ChannelDispatcher cd in host.ChannelDispatchers)
{
Console.WriteLine( " --------------------当前信道分发器信息--------------------\n " );
Console.WriteLine( " 监听的物理地址:{0}\n " ,cd.Listener.Uri.ToString());
Console.WriteLine( " 终结点列表:\n " );
foreach (EndpointDispatcher ed in cd.Endpoints)
{
Console.WriteLine( " 终结点逻辑地址:{0} " ,ed.EndpointAddress.Uri.ToString());
}
Console.WriteLine();
}
Console.WriteLine();
}
代码中的DisplayAllEndPointLogicalAndPhysicalUri方法中,我使用了ChannelDispatcher对象中的Endpoints属性,这个属性是EndpointDispatcher对象的集合,使用这个属性就可以获取信道分派器所关联的所有终结点分派器。接下来看一下服务端的配置文件:
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
< service name ="WCF_Study1.ServiceContracts.SystemInfoService"
behaviorConfiguration ="metaBehavior" >
< endpoint address ="SystemInfoService"
listenUri ="http://localhost:8888/SystemInfoService"
binding ="wsHttpBinding"
contract ="WCF_Study1.ServiceContracts.ISystemInfoServiceContract" />
< endpoint address ="SystemInfoServiceV3"
listenUri ="http://localhost:8888/SystemInfoService"
binding ="wsHttpBinding"
contract ="WCF_Study1.ServiceContracts.ISystemInfoServiceContract" />
<!--
<endpoint address="SystemInfoServiceV5"
listenUri="net.tcp://localhost:8111/SystemInfoService"
binding="wsHttpBinding"
contract="WCF_Study1.ServiceContracts.ISystemInfoServiceContract" />
当逻辑地址与物理地址协议不一致时,启动服务宿主程序会马上抛出异常
-->
< host >
< baseAddresses >
< add baseAddress ="http://localhost:8000" />
</ baseAddresses >
</ host >
</ service >
</ services >
上面的配置代码中,每个<endpoint/>节点都设置了一个新出现的属性——listenerUri,通过这个属性设置的就是每个服务端终结点使用的物理地址,也就是真正监听服务调用请求消息的地址。从刚刚服务宿主程序的代码可以知道,这个地址可以通过ChannelDispatcher对象的Listener属性获取ChannerListener对象,再从其Uri属性获取。相信来到这一步,读者也理解了,每个服务端终结点都可以使用相同的物理地址,因为物理地址这个概念其实是属于信道监听器的,而不是终结点的。
客户端程序的代码没有任何特别,就是简单地构造服务代理,然后调用服务,因此就不贴上了;但客户端配置文件中有比较有趣的地方:
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
< endpointBehaviors >
< behavior name ="clientEndpointBehavior" >
<!-- <clientVia viaUri="http://localhost:8888/SystemInfoService"/> -->
</ behavior >
</ endpointBehaviors >
</ behaviors >
<!-- 其他无关配置 -->
< client >
< endpoint address ="http://localhost:8000/SystemInfoService"
behaviorConfiguration ="clientEndpointBehavior"
binding ="wsHttpBinding" bindingConfiguration ="WSHttpBinding_ISystemInfoServiceContract"
contract ="Services.ISystemInfoServiceContract" name ="WSHttpBinding_ISystemInfoServiceContract" >
< identity >
< userPrincipalName value ="KL-THINK\KL" />
</ identity >
<!--
BasicHttpBinding要求物理地址与逻辑地址必须相一致,因此只能使用wsHttpBinding来实现分离。
-->
</ endpoint >
</ client >
在客户端配置文件中,在终结点行为集合(<endpointBehaviors></endpointBehaviors>)中设置了一个名为clientEndpointBehavior的行为,该行为提供了设置一个<clientVia/>节点,该节点的作用就是控制客户端消息在最终到达目标终结点前,必须通过的物理监听地址(因此取名为viaUri)。当客户端终结点使用了该行为,那么客户端就能正常进行远程服务调用,但如果没有设置这个“中转站”地址,那么当调用远程服务时就会抛出以下异常:
最后一个尝试就是部署一个终结点,其逻辑地址和物理地址的协议不一致,也就是服务端配置文件中被注释掉的那个终结点。当取消注释后,启动服务宿主程序将会抛出以下异常: