wcf基础知识之ListenUri和ListenUriMode实现 逻辑地址和物理地址的分离
在上一篇博客中介绍了TcpTrace的使用,并且说到要使用TcpTrace最简单的方法就是设置ClientViaBehavior的viaUri的值。但是没有说这个值表示的是什么意思?其实这个值表示的物理地址。其实在物理地址和逻辑地址分离的方面包括两方面的内容:服务端的物理地址和逻辑地址的分离以及客户端的物理地址和逻辑地址的分离,这个如果配合TcpTrace可以很好的说明结果。
今天我们要说的是服务端的物理地址和逻辑地址的分离,因为客户端的分离是通过Clientvia实现的,所以那个很简单。要说服务端的物理地址和逻辑地址的分离,不得不说两个功臣:ListenUri和ListenUriMode。为什么说他俩是功臣呢?因为ListenUri指定了服务的物理地址,大家可能觉得奇怪,我们设置服务地址的时候一般都是设置Address(ABC的一部分),但是Address指定的是逻辑地址,而ListenUri指定的是物理地址,如果指定了这个属性的值,那么就指定了服务的物理地址?其实这么说有点小问题,因为决定服务真正物理地址的还有一个Enum 枚举值ListenuriMode:它包括Explict和Unique两个值。
Explict表示严格的按照ListenUri设置的值作为服务的监听地址,也就是物理地址。
Unique则有点区别,它表示的是唯一,也就是根据一定的规则来定义服务的物理地址。具体规则是什么呢?
- 如果采用TCp作为传输协议,并且实现了端口共享(wcf基础知识之端口共享 portSharing)的话,那么端口号是不可以更改,会在地址的末尾添加GUID值保证地址的唯一性
- 如果采用的还是TCP协议,没有实现端口共享,那么wcf会更改端口号为服务器中一个没有使用的端口作为端口号,不会在末尾添加GUID值
- 如果采用的是Http等协议的话,不存在端口共享的概念,那么wcf会在地址末尾添加GUID值作为物理地址保证唯一性。
既然说到了ListenUri,那么我们必须通过实例说明一下,在采用不同的协议并且ListenUriMode的取值不同的情况下,最后服务的监听地址是什么样的?
在这里我要说明一下,所谓的物理地址和逻辑地址分离的真正含义就是让他们的值不同而已,就是监听地址和逻辑地址不同。因为服务的监听地址是他们的物理地址。默认情况下服务的物理地址和逻辑地址是一样的,并且ListenUriMode的取值为Explict。
我们采用Http和Tcp两种协议,ListenUriMode的取值有两种,Tcp是否采用端口共享有两种情况,那么一共有六种情况。我们来举例说明最终的结果是否和我们的预想一致。
1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3 <system.serviceModel>
4 <services>
5
6 <service name="Chinaer.WcfDemo.Services.GuoService">
7 <!--采用Http协议 ListenUriMode=Explict 最后的结果应该是http://127.0.0.1:6666/cal1-->
8 <endpoint listenUri="http://127.0.0.1:6666/cal1" listenUriMode="Explicit" address="http://127.0.0.1:88/guoservic2e" binding="basicHttpBinding" contract="Chinaer.WcfDemo.Contracts.IGuo"></endpoint>
9 <!--采用Http协议 ListenUriMode=Unique 最后的结果应该是http://127.0.0.1:6666/cal2/GUID的值-->
10 <endpoint listenUri="http://127.0.0.1:6667/cal2" listenUriMode="Unique" address="http://127.0.0.1:8888/guoservic2e" binding="basicHttpBinding" contract="Chinaer.WcfDemo.Contracts.IGuo"></endpoint>
11 <!--采用TCp协议 ListenUriMode=Explict 最后的结果应该是net.tcp://127.0.0.1:8218/guoservic2e1-->
12 <endpoint address="net.tcp://127.0.0.1:8218/guoservic2e1" listenUriMode="Explicit" binding="netTcpBinding" contract="Chinaer.WcfDemo.Contracts.IGuo"></endpoint>
13 <!--采用TCp协议 ListenUriMode=Unique 最后的结果应该是net.tcp://127.0.0.1:端口号会更改为服务器未使用的端口号/guoservic2e2-->
14 <endpoint address="net.tcp://127.0.0.1:8821/guoservic2e2" binding="netTcpBinding" listenUriMode="Unique" contract="Chinaer.WcfDemo.Contracts.IGuo"></endpoint>
15 <!--采用Tcp协议 采用端口共享 ListenUriMode=Unique 最后的结果应该是net.tcp://127.0.0.1:8822/guoservic2e3/GUID的值-->
16 <endpoint address="net.tcp://127.0.0.1:8822/guoservic2e3" bindingConfiguration="portSharingBinding" binding="netTcpBinding" listenUriMode="Unique" contract="Chinaer.WcfDemo.Contracts.IGuo"></endpoint>
17 <!--采用Tcp协议 采用端口共享 ListenUriMode=Explict 最后的结果应该是net.tcp://127.0.0.1:8822/guoservic2e4-->
18 <endpoint address="net.tcp://127.0.0.1:8822/guoservic2e4" bindingConfiguration="portSharingBinding" binding="netTcpBinding" listenUriMode="Explicit" contract="Chinaer.WcfDemo.Contracts.IGuo"></endpoint>
19 </service>
20 </services>
21
22 <behaviors></behaviors>
23
24 <bindings>
25 <netTcpBinding>
26 <binding name="portSharingBinding" portSharingEnabled="true"></binding>
27 </netTcpBinding>
28 </bindings>
29
30 </system.serviceModel>
31
32 </configuration>
配置文件我们对于同一个服务配置了六种不同的终结点,其中我在注释中有写明最后会显示的监听地址,我们可以一起对照看看是否正确。
1 using (ServiceHost guoHost = new ServiceHost(typeof(Chinaer.WcfDemo.Services.GuoService)))
2 {
3
4 //// Chinaer.WcfDemo.Contracts.IGuo
5 //if (guoHost.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
6 //{
7 // ServiceMetadataBehavior metaDataBehavior = new ServiceMetadataBehavior();
8 // metaDataBehavior.HttpGetEnabled = true;
9 // metaDataBehavior.HttpGetUrl = new Uri("http://127.0.0.1:8956/guoService/mex");
10 // guoHost.Description.Behaviors.Add(metaDataBehavior);
11 //}
12 guoHost.Opened += delegate { Console.WriteLine("guoService Open"); };
13 try
14 {
15 if (guoHost.State != CommunicationState.Opened || guoHost.State != CommunicationState.Opening)
16 {
17 guoHost.Open();
18 int i = 0;
19 foreach (ChannelDispatcher channelDispatcher in guoHost.ChannelDispatchers)
20 {
21 Console.WriteLine("{0}:{1}",++i,channelDispatcher.Listener.Uri);
22 }
23 Console.Read();
24 }
25 }
26 catch (CommunicationException ex)
27 {
28 guoHost.Abort();
29 }
30 catch (Exception ex)
31 {
32 guoHost.Abort();
33 }
34 }
我们获取监听地址的方式是通过ServiceHost 服务宿主的实例,获取它的信道分发器,关于信道分发器,其实还是一个比较有意思的东西,其中包括服务端信道和客户端信道,其实作用都相同,只是叫法不同而已,以后有机会我们再聊。
通过信道分发器可以获取到服务的监听地址,我们通过客户端打印显示。
说实话,为了避免说错,我特意对了好几遍这几个地址,大家有兴趣可以实验一下是否我说的正确。
服务端的物理地址和逻辑地址的分离就是这些,就是实现物理地址和逻辑地址的不同,从而实现特定的场景我们不能提供真正的物理地址,例如负载均衡,当然我没有做过那么大的项目,不知道中介服务如何保证的,不再赘述。
还有一个忘了说,我们应该通过TcpTrace监听一个物理地址和逻辑地址分离后的soap。
1 <service name="Chinaer.WcfDemo.Services.GuoService" behaviorConfiguration="Chinaer.WcfDemo.Services.GuoService">
2 <!--采用Http协议 ListenUriMode=Explict 最后的结果应该是http://127.0.0.1:6666/cal1-->
3 <endpoint listenUri="http://127.0.0.1:8888/guoservic2e" listenUriMode="Explicit"
4 address="http://127.0.0.1:9999/guoservic2e"
5 binding="wsHttpBinding"
6 contract="Chinaer.WcfDemo.Contracts.IGuo" bindingConfiguration="myBinding"></endpoint>
7
8 <!--采用Http协议 ListenUriMode=Unique 最后的结果应该是http://127.0.0.1:6666/cal2/GUID的值-->
9 <!--<endpoint listenUri="http://127.0.0.1:6667/cal2" listenUriMode="Unique" address="http://127.0.0.1:8888/guoservic2e" binding="basicHttpBinding" contract="Chinaer.WcfDemo.Contracts.IGuo"></endpoint>
10 --><!--采用TCp协议 ListenUriMode=Explict 最后的结果应该是net.tcp://127.0.0.1:8218/guoservic2e1--><!--
11 <endpoint address="net.tcp://127.0.0.1:8218/guoservic2e1" listenUriMode="Explicit" binding="netTcpBinding" contract="Chinaer.WcfDemo.Contracts.IGuo"></endpoint>
12 --><!--采用TCp协议 ListenUriMode=Unique 最后的结果应该是net.tcp://127.0.0.1:端口号会更改为服务器未使用的端口号/guoservic2e2--><!--
13 <endpoint address="net.tcp://127.0.0.1:8821/guoservic2e2" binding="netTcpBinding" listenUriMode="Unique" contract="Chinaer.WcfDemo.Contracts.IGuo"></endpoint>
14 --><!--采用Tcp协议 采用端口共享 ListenUriMode=Unique 最后的结果应该是net.tcp://127.0.0.1:8822/guoservic2e3/GUID的值--><!--
15 <endpoint address="net.tcp://127.0.0.1:8822/guoservic2e3" bindingConfiguration="portSharingBinding" binding="netTcpBinding" listenUriMode="Unique" contract="Chinaer.WcfDemo.Contracts.IGuo"></endpoint>
16 --><!--采用Tcp协议 采用端口共享 ListenUriMode=Explict 最后的结果应该是net.tcp://127.0.0.1:8822/guoservic2e4--><!--
17 <endpoint address="net.tcp://127.0.0.1:8822/guoservic2e4" bindingConfiguration="portSharingBinding" binding="netTcpBinding" listenUriMode="Explicit" contract="Chinaer.WcfDemo.Contracts.IGuo"></endpoint>-->
18 </service>
19 </services>
Address地址端口为9999,而ListenUri为8888.
我们在客户端调用服务端操作,然后我们在TcpTrace中查看到的消息就是:
大家注意soap消息的to报头的端口号为9999,是逻辑地址。