在WCF中,每个终结点都包含两个不同的地址——逻辑地址和物理地址。逻辑地址就是终结点Address属性表示的地址。至于物理地址,对于消息发送放来讲,就是消息被真正发送的目的地址;而对于消息的接收放来讲,就是监听器真正监听的地址。
一、服务端的物理地址
在默认的情况下,终结点的逻辑地址和物理地址是同一个URI。换句话说,终结的逻辑地址是必须的,如何物理地址没有指定的,默认使用逻辑地址作为物理地址。对于消息接收方的终结点来讲,物理地址就是监听地址,通过ServiceEndpoint的ListenUri表示:
1: //---------------------------------------------------------------<!--CRLF-->
2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan<!--CRLF-->
3: //---------------------------------------------------------------<!--CRLF-->
4: public class ServiceEndpoint<!--CRLF-->
5: {
<!--CRLF-->
6: ... ...
<!--CRLF-->
7: public Uri ListenUri { get; set; }<!--CRLF-->
8: }
<!--CRLF-->
在对服务进行寄宿的时候,我们可以调用SeriviceHostBase或者ServiceHost的AddServiceEndpoint对应的重载来为添加的终结点指定ListenUri:
1: //---------------------------------------------------------------<!--CRLF-->
2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan<!--CRLF-->
3: //---------------------------------------------------------------<!--CRLF-->
4: public abstract class ServiceHostBase : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable<!--CRLF-->
5: {
<!--CRLF-->
6: //... ...<!--CRLF-->
7: public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri);<!--CRLF-->
8: public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri);<!--CRLF-->
9: }
<!--CRLF-->
10:
<!--CRLF-->
11: public class ServiceHost : ServiceHostBase<!--CRLF-->
12: {
<!--CRLF-->
13: //... ...<!--CRLF-->
14: public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri);<!--CRLF-->
15: public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri);<!--CRLF-->
16: }
<!--CRLF-->
17:
<!--CRLF-->
在下面的代码片断中,就为终结点指定了一个同于逻辑地址的物理地址(ListenUri):
1: //---------------------------------------------------------------<!--CRLF-->
2: // ListenUri.cs (c) by 2008 Jiang Jin Nan<!--CRLF-->
3: //---------------------------------------------------------------<!--CRLF-->
4: using (ServiceHost serviceHost = new ServiceHost(typeof(CalculateService)))<!--CRLF-->
5: {
<!--CRLF-->
6: serviceHost.AddServiceEndpoint(typeof(ICalculate),new WSHttpBinding(),<!--CRLF-->
7: "http://127.0.0.1:9999/calculateservice",<!--CRLF-->
8: new Uri ("http://127.0.0.1:8888/calculateservice"));<!--CRLF-->
9: Console.Read();
<!--CRLF-->
10: }
<!--CRLF-->
11:
<!--CRLF-->
当然,ListenUri也可以通过配置进行指定,下面的配置和上面的代码是等效的:
1: <configuration><!--CRLF-->
2: <system.serviceModel><!--CRLF-->
3: <services><!--CRLF-->
4: <service name="Artech.WcfServices.Services.CalculateService"><!--CRLF-->
5: <endpoint binding="wsHttpBinding"<!--CRLF-->
6: contract="Artech.WcfServices.Contracts.ICalculate" address="http://127.0.0.1:8888/calculateservice"<!--CRLF-->
7: listenUri="http://127.0.0.1:8888/calculateservice" /><!--CRLF-->
8: </service><!--CRLF-->
9: </services><!--CRLF-->
10: </system.serviceModel><!--CRLF-->
11: </configuration><!--CRLF-->
12:
<!--CRLF-->
二、客户端的物理地址
上面我们介绍了基于消息接收端终结点物理地址的指定,现在我们来介绍对于消息发送端的终结点,物理地址如何指定。在上面我们说过,对于消息的发送端来讲,物理地址其实就是消息发送的真正目的地址。该地址通过一个特殊的EndpointBehavior,ClientViaBehavor来指定。ClientViaBehavor定义的Uri代表该物理地址。
1: //---------------------------------------------------------------<!--CRLF-->
2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan<!--CRLF-->
3: //---------------------------------------------------------------<!--CRLF-->
4: public class ClientViaBehavior : IEndpointBehavior<!--CRLF-->
5: {
<!--CRLF-->
6: //... ...<!--CRLF-->
7: public Uri Uri { get; set; }<!--CRLF-->
8: }
<!--CRLF-->
ClientViaBehavor是WCF自定的EndpointBehavior, 我们可以通过下面的配置应用该ClientViaBehavor。通过<endpointBehaviors>下的<clientVia〉配置节,通过viaUri设置了一个不同于终结点地址(http://127.0.0.1:9999/calculateservice)的物理地址:http://127.0.0.1:8888/calculateservice。
1: <?xml version="1.0" encoding="utf-8" ?><!--CRLF-->
2: <configuration><!--CRLF-->
3: <system.serviceModel><!--CRLF-->
4: <behaviors><!--CRLF-->
5: <endpointBehaviors><!--CRLF-->
6: <behavior name="clientViaBehavior"><!--CRLF-->
7: <clientVia viaUri="http://127.0.0.1:8888/calculateservice" /><!--CRLF-->
8: </behavior><!--CRLF-->
9: </endpointBehaviors><!--CRLF-->
10: </behaviors><!--CRLF-->
11: <client><!--CRLF-->
12: <endpoint address="http://127.0.0.1:9999/calculateservice" behaviorConfiguration="clientViaBehavior"<!--CRLF-->
13: binding="wsHttpBinding" bindingConfiguration="" contract="Artech.WcfServices.Contracts.ICalculate"<!--CRLF-->
14: name="calculateservice"><!--CRLF-->
15: </endpoint><!--CRLF-->
16: </client><!--CRLF-->
17: </system.serviceModel><!--CRLF-->
18: </configuration><!--CRLF-->
三、ListenUri和ListenUriMode
上面我们介绍了终结点的ListenUri属性用于指定一个用于网络监听的物理地址,我们接下来讨论与ListenUri相关的另一个概念——ListenUriMode。ListenUriMode代表的是确定真正监听地址的模式。ListenUriMode通过System.ServiceModel.Description.ListenUriMode枚举表示,而ListenUriMode定义了两个枚举值:Explicit和Unique。
1: //---------------------------------------------------------------<!--CRLF-->
2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan<!--CRLF-->
3: //---------------------------------------------------------------<!--CRLF-->
4: public enum ListenUriMode<!--CRLF-->
5: {
<!--CRLF-->
6: Explicit,
<!--CRLF-->
7: Unique
<!--CRLF-->
8: }
<!--CRLF-->
ListenUriMode.Explicit表示显示采用终结点ListenUri属性设置的Uri作为最终的监听地址;而Unique则根据ListenUri采用不同的策略保证最终使用的监听地址是唯一的。而对于如何确保监听地址的唯一性,WCF采用如下的策略:
- 如果采用TCP作为传输协议,在不采用端口共享的情况下,会选择一个未被使用的端口作为最终监听地址的端口一确保地址的唯一性
- 如果采用TCP作为传输协议,同时采用端口共享情况下,会添加一个GUID作为后缀以确保地址的唯一性
- 对于非TCP作为传输协议,会添加一个GUID作为后缀以确保地址的唯一性
在ServiceEndpoint中,定义了一个ListenUriMode属性,用于指定终结点的ListenUriMode。
1: //---------------------------------------------------------------<!--CRLF-->
2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan<!--CRLF-->
3: //---------------------------------------------------------------<!--CRLF-->
4: public class ServiceEndpoint<!--CRLF-->
5: {
<!--CRLF-->
6: //... ...<!--CRLF-->
7: public Uri ListenUri { get; set; }<!--CRLF-->
8: public ListenUriMode ListenUriMode { get; set; }<!--CRLF-->
9: }
<!--CRLF-->
10:
<!--CRLF-->
在对服务进行寄宿的时候,我们可以通过代码的方式为添加的终结点指定ListenUriMode。下面的代码将终结点设置成ListenUriMode.Unique.