在WCF中,每個終結點都包含兩個不同的地址——邏輯地址和物理地址。邏輯地址就是終結點Address屬性表示的地址。至於物理地址,對於消息發送放來講,就是消息被真正發送的目的地址;而對於消息的接收放來講,就是監聽器真正監聽的地址。
1、服務端的物理地址
在默認的情況下,終結點的邏輯地址和物理地址是同一個URI。換句話說,終結的邏輯地址是必須的,如何物理地址沒有指定的,默認使用邏輯地址作為物理地址。對於消息接收方的終結點來講,物理地址就是監聽地址,通過ServiceEndpoint的ListenUri表示:
//---------------------------------------------------------------
// EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
//---------------------------------------------------------------
public class ServiceEndpoint
{
... ...
public Uri ListenUri { get; set; }
}
在對服務進行寄宿的時候,我們可以調用SeriviceHostBase或者ServiceHost的AddServiceEndpoint對應的重載來為添加的終結點指定ListenUri:
//---------------------------------------------------------------
// EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
//---------------------------------------------------------------
public abstract class ServiceHostBase : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable
{
... ...
public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri);
public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri);
}
public class ServiceHost : ServiceHostBase
{
... ...
public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri);
public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri);
}
在下面的代碼片斷中,就為終結點指定了一個同於邏輯地址的物理地址(ListenUri):
//---------------------------------------------------------------
// ListenUri.cs (c) by 2008 Jiang Jin Nan
//---------------------------------------------------------------
using (ServiceHost serviceHost = new ServiceHost(typeof(CalculateService)))
{
serviceHost.AddServiceEndpoint(typeof(ICalculate),new WSHttpBinding(),
"http://127.0.0.1:9999/calculateservice",
new Uri ("http://127.0.0.1:8888/calculateservice"));
Console.Read();
}
當然,ListenUri也可以通過配置進行指定,下面的配置和上面的代碼是等效的:
<configuration>
<system.serviceModel>
<services>
<service name="Artech.WcfServices.Services.CalculateService">
<endpoint binding="wsHttpBinding"
contract="Artech.WcfServices.Contracts.ICalculate" address="http://127.0.0.1:8888/calculateservice"
listenUri="http://127.0.0.1:8888/calculateservice" />
</service>
</services>
</system.serviceModel>
</configuration>
2、客戶端的物理地址
上面我們介紹了基於消息接收端終結點物理地址的指定,現在我們來介紹對於消息發送端的終結點,物理地址如何指定。在上面我們說過,對於消息的發送端來講,物理地址其實就是消息發送的真正目的地址。該地址通過一個特殊的EndpointBehavior,ClientViaBehavor來指定。ClientViaBehavor定義的Uri代表該物理地址。
//---------------------------------------------------------------
// EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
//---------------------------------------------------------------
public class ClientViaBehavior : IEndpointBehavior
{
... ...
public Uri Uri { get; set; }
}
ClientViaBehavor是WCF自定的EndpointBehavior, 我們可以通過下面的配置應用該ClientViaBehavor。通過<endpointBehaviors>下的<clientVia〉配置節,通過viaUri設置了一個不同於終結點地址(http://127.0.0.1:9999/calculateservice)的物理地址:http://127.0.0.1:8888/calculateservice。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="clientViaBehavior">
<clientVia viaUri="http://127.0.0.1:8888/calculateservice" />
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="http://127.0.0.1:9999/calculateservice" behaviorConfiguration="clientViaBehavior"
binding="wsHttpBinding" bindingConfiguration="" contract="Artech.WcfServices.Contracts.ICalculate"
name="calculateservice">
</endpoint>
</client>
</system.serviceModel>
</configuration>
3、ListenUri和ListenUriMode
上面我們介紹了終結點的ListenUri屬性用於指定一個用於網絡監聽的物理地址,我們接下來討論與ListenUri相關的另一個概念——ListenUriMode。ListenUriMode代表的是確定真正監聽地址的模式。ListenUriMode通過System.ServiceModel.Description.ListenUriMode枚舉表示,而ListenUriMode定義了兩個枚舉值:Explicit和Unique。
//---------------------------------------------------------------
// EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
//---------------------------------------------------------------
public enum ListenUriMode
{
Explicit,
Unique
}
ListenUriMode.Explicit表示顯示采用終結點ListenUri屬性設置的Uri作為最終的監聽地址;而Unique則根據ListenUri采用不同的策略保證最終使用的監聽地址是唯一的。而對於如何確保監聽地址的唯一性,WCF采用如下的策略:
如果采用TCP作為傳輸協議,在不采用端口共享的情況下,會選擇一個未被使用的端口作為最終監聽地址的端口一確保地址的唯一性
如果采用TCP作為傳輸協議,同時采用端口共享情況下,會添加一個GUID作為後綴以確保地址的唯一性
對於非TCP作為傳輸協議,會添加一個GUID作為後綴以確保地址的唯一性
在ServiceEndpoint中,定義了一個ListenUriMode屬性,用於指定終結點的ListenUriMode。
//---------------------------------------------------------------
// EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
//---------------------------------------------------------------
public class ServiceEndpoint
{
... ...
public Uri ListenUri { get; set; }
public ListenUriMode ListenUriMode { get; set; }
}
在對服務進行寄宿的時候,我們可以通過代碼的方式為添加的終結點指定ListenUriMode。下面的代碼將終結點設置成ListenUriMode.Unique.
//---------------------------------------------------------------
// EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
//---------------------------------------------------------------
using (ServiceHost serviceHost = new ServiceHost(typeof(CalculateService)))
{
ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint(typeof(ICalculate), new WSHttpBinding(),
http://127.0.0.1:9999/calculateservice, new Uri("http://127.0.0.1:8888/calculateservice"));
endpoint.ListenUriMode = ListenUriMode.Unique;
Console.Read();
}
ListenUriMode也可以通過配置的方式進行指定,下面的配置和上面的代碼是等效的。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="Artech.ListenUriDemos.Services.CalculateService">
<endpoint address="http://127.0.0.1:9999/calculateservice" binding="wsHttpBinding" contract="Artech.ListenUriDemos.Contracts.ICalculate"
listenUriMode="Unique" listenUri="http://127.0.0.1:8888/calculateservice" />
</service>
</services>
</system.serviceModel>
</configuration>
為了驗證ListenUriMode.Unique模式下,我寫了下面一個簡單的例子:在對服務(Artech.ListenUriDemos.Services.CalculateService)進行寄宿的時候,為之添加了如下5個終結點,具體的配置如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="PortSharingBinding" portSharingEnabled="true" />
</netTcpBinding>
</bindings>
<services>
<service name="Artech.ListenUriDemos.Services.CalculateService">
<!--1. BasicHttpBinding & ListenUriMode.Explicit-->
<endpoint address="http://127.0.0.1:5555/service1" binding="basicHttpBinding"
name="httpExplicitListenUriMode" contract="Artech.ListenUriDemos.Contracts.ICalculate" />
<!--2. BasicHttpBinding & ListenUriMode.Unique-->
<endpoint address="http://127.0.0.1:6666/service2" binding="basicHttpBinding"
name="httpUniquListenUriMode" contract="Artech.ListenUriDemos.Contracts.ICalculate"
listenUriMode="Unique" />
<!--3. NetTcpBinding & ListenUriMode.Explicit-->
<endpoint address="net.tcp://127.0.0.1:7777/service3" binding="netTcpBinding"
bindingConfiguration="" name="tcpExplicitListenUriMode" contract="Artech.ListenUriDemos.Contracts.ICalculate" />
<!--4. NetTcpBinding & ListenUriMode.Unique-->
<endpoint address="net.tcp://127.0.0.1:8888/service4" binding="netTcpBinding"
bindingConfiguration="" name="tcpUniquListenUriMode" contract="Artech.ListenUriDemos.Contracts.ICalculate"
listenUriMode="Unique" />
<!--5. NetTcpBinding & ListenUriMode.Unique & Port Sharing-->
<endpoint address="net.tcp://127.0.0.1:9999/service5" binding="netTcpBinding"
bindingConfiguration="PortSharingBinding" name="tcpPortSharingUniquListenUriMode"
contract="Artech.ListenUriDemos.Contracts.ICalculate" listenUriMode="Unique" />
</service>
</services>
</system.serviceModel>
</configuration>
在一個控制台應用程序中,通過下面的代碼實現對服務的寄宿。然後遍歷ServiceHost的ChannelDispatcher列表,並將ChannelDispatcher對象的ChannelListener的Uri打印出來:
//---------------------------------------------------------------
// EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
//---------------------------------------------------------------
using System;
using System.ServiceModel;
using Artech.ListenUriDemos.Services;
using System.ServiceModel.Dispatcher;
namespace Artech.ListenUriDemos.Hosting
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost serviceHost = new ServiceHost(typeof(CalculateService)))
{
serviceHost.Open();
int i = 0;
foreach (ChannelDispatcher channelDispatcher in serviceHost.ChannelDispatchers)
{
Console.WriteLine("第{0}個終結點的監聽地址為: {1}", ++i,channelDispatcher.Listener.Uri);
}
Console.Read();
}
}
}
}
最終的輸出如下,從中我們可以看出,對於ListenUriMode.Unique的三個終結點:第2、4、5個,第2個終結點采用了基於HTTP的BasicHttpBinding,WCF通過加了一個GUID後綴確保監聽地址的唯一性;使用了基於NetTcpBinding的第4個終結點,通過使用一個可用的端口(1119)確保監聽地址的唯一性;而對於通過采用了NetTcpBinding的第5個終結點,由於采用了端口共享,不能改變其端口,所以仍然采用添加GUID後綴的方式確保監聽地址的唯一性。
第1個終結點的監聽地址為: http://127.0.0.1:5555/service1
第2個終結點的監聽地址為: http://127.0.0.1:6666/service2/d9ce6f30-3103-4ec9-b73b-34f32c65b0a1
第3個終結點的監聽地址為: net.tcp://127.0.0.1:7777/service3
第4個終結點的監聽地址為: net.tcp://127.0.0.1:1119/service4
第5個終結點的監聽地址為: net.tcp://127.0.0.1:9999/service5/b4f69288-913b-43ec-8e42-e58f150ee91c