wcf基础知识之ListenUri和ListenUriMode实现 逻辑地址和物理地址的分离

简介: 在上一篇博客中介绍了TcpTrace的使用,并且说到要使用TcpTrace最简单的方法就是设置ClientViaBehavior的viaUri的值。但是没有说这个值表示的是什么意思?其实这个值表示的物理地址。

在上一篇博客中介绍了TcpTrace的使用,并且说到要使用TcpTrace最简单的方法就是设置ClientViaBehavior的viaUri的值。但是没有说这个值表示的是什么意思?其实这个值表示的物理地址。其实在物理地址和逻辑地址分离的方面包括两方面的内容:服务端的物理地址和逻辑地址的分离以及客户端的物理地址和逻辑地址的分离,这个如果配合TcpTrace可以很好的说明结果。

今天我们要说的是服务端的物理地址和逻辑地址的分离,因为客户端的分离是通过Clientvia实现的,所以那个很简单。要说服务端的物理地址和逻辑地址的分离,不得不说两个功臣:ListenUri和ListenUriMode。为什么说他俩是功臣呢?因为ListenUri指定了服务的物理地址,大家可能觉得奇怪,我们设置服务地址的时候一般都是设置Address(ABC的一部分),但是Address指定的是逻辑地址,而ListenUri指定的是物理地址,如果指定了这个属性的值,那么就指定了服务的物理地址?其实这么说有点小问题,因为决定服务真正物理地址的还有一个Enum 枚举值ListenuriMode:它包括Explict和Unique两个值。

Explict表示严格的按照ListenUri设置的值作为服务的监听地址,也就是物理地址。

Unique则有点区别,它表示的是唯一,也就是根据一定的规则来定义服务的物理地址。具体规则是什么呢?

  1. 如果采用TCp作为传输协议,并且实现了端口共享(wcf基础知识之端口共享 portSharing)的话,那么端口号是不可以更改,会在地址的末尾添加GUID值保证地址的唯一性
  2. 如果采用的还是TCP协议,没有实现端口共享,那么wcf会更改端口号为服务器中一个没有使用的端口作为端口号,不会在末尾添加GUID值
  3. 如果采用的是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,是逻辑地址。

 

我又回来了,回到了技术最前线,
相关文章
|
网络协议 安全 开发者
|
网络架构
wcf基础知识之完结
在我以前的wcf博客中,我倾向于通过一个具体的问题来引出一个wcf的技术知识点,这得到了园友们的支持,我在这里向支持我的朋友们说声谢谢,谢谢你们的支持。但是也得到了很多的建议,今天有园友通过QQ联系我说,我讲的内容虽然说有知识点,但是知识点太分散,不能完整的理解wcf的各个知识点的作用以及相互之间的联系,我听后觉得很有道理,所以我放弃我以前的分散写wcf知识点的做法,改成一个系列一个系列的进行,这样可以让大家更好的更完整的掌握wcf的精髓。
786 0
|
XML 网络架构 数据格式
wcf 基础知识 之 消息交换模式 response/reply oneway deplex
wcf支持请求-回复(response-reply)、单工(oneway)以及双工(duplex)三种消息交换模式。今天我们来说一下第一种消息交换模式--请求--回复,请注意这里是消息交换模式。 请求-回复消息交换模式是默认的wcf通信方式,如果我们不显式的设定消息交换模式,那么它就是response-reply模式。
803 0
|
XML 网络协议 数据格式
wcf基础知识之端口共享 portSharing
现在时间已经是凌晨一点了,我准备了端口共享的内容,但是因为时间太晚,明天还要上班,所以我们就不长篇大徐了,剪短的说明一下内容,让大家明白就可以了。 今天来说一下端口共享,什么是端口共享呢?在wcf中,所谓的端口共享其实就是一个服务的地址为http://127.0.0.1:80/calService,而另一个服务的地址也为http:127.0.0.1:80/weatherService,但是端口是一样的,在wcf中这其实是不能运行的。
695 0
|
网络架构 程序员
wcf基础知识之 查看soap消息 TcpTrace
今天本来准备了wcf的三种消息模式 wcf 基础知识 之 消息交换模式 response/reply oneway deplex  的介绍,但是考虑到里面有说到soap消息,但是可能还有一些朋友对如何查看soap消息还不是很清楚,所以这次先来把如何查看wcf通信的介质--soap消息介绍一下。
990 0
|
前端开发 Windows 机器人
wcf 基础教程 第一讲 wcf基础知识
最近工作有点忙,感觉有点累啊,很久没有更新博客了,看到自己的博客访问量日渐下降,我于心不忍,决定还是重新开博来吸引大家的关注。这次要说的不是别的,正是大名鼎鼎的wcf。或者在中间我会穿插MVC的说明,毕竟上一个系列MVC也没有写完就过年了,这次希望能坚持到底。
951 0
wcf 基础教程 第一讲 wcf基础知识 在IIS中承载wcf服务
在上一篇博客wcf 基础教程 第一讲 wcf基础知识中,因为篇幅过长,所以我选择在这一篇把wcf用IIS承载的方式介绍一下。 首先我们在WebHosting web项目中创建一个.svc的文件,这里我建议之间添加一个类,然后把文件后缀改成.svc就可以。
966 0
|
10月前
|
前端开发
WCF更新服务引用报错的原因之一
WCF更新服务引用报错的原因之一