概述
在WCF中,每個服務終結點都與兩個地址相關聯,一個邏輯地址和一 個物理地址,邏輯地址就是SOAP消息的目標地址,即前面不止一次提到的 “To”地址,而物理地址是WCF偵聽器真正監聽的地址。在WCF中,邏 輯地址稱之為終結點地址Endpoint Address,而物理地址則稱之為監聽地址 ListenUri。
兩種地址
WCF中,物理地址負責使用特定的傳輸協議在特 定的位置接收傳入的消息,除非特別指定,否則邏輯地址將被用來做物理地址, 換句話說,在以前我們對於終結點所配置的EndpointAddress都是指定了邏輯地 址,如我們的服務端配置如下:
<endpoint address="http://localhost:8887/CalculatorService1"
binding ="wsHttpBinding"
contract="TerryLee.WCFAddressing.Contract.ICalculator"></endpoint>
<endpoint address="http://localhost:8887/CalculatorService2"
binding ="basicHttpBinding"
contract="TerryLee.WCFAddressing.Contract.ICalculator"></endpoint>
現在我們輸出一下,就可以看到兩個地址是 同樣的值,如下代碼所示:
using (ServiceHost calculatorServiceHost =
new ServiceHost(typeof (CalculatorService)))
{
calculatorServiceHost.Opened += delegate
{
Console.WriteLine("Service begin to listen via the Address:{0}",
calculatorServiceHost.BaseAddresses[0].ToString());
};
calculatorServiceHost.Open();
foreach (ServiceEndpoint se in calculatorServiceHost.Description.Endpoints)
{
Console.WriteLine("Endpoint details:");
Console.WriteLine("Logical address: \t{0}", se.Address);
Console.WriteLine("Physical address: \t{0}", se.ListenUri);
Console.WriteLine("Binding: \t{0} ", se.Binding.Name);
Console.WriteLine();
}
Console.Read();
}
輸出結果如圖1所示:
圖1
設定物理地址
前面我們輸出的結果邏輯地址和物理地址 是相同的,我們可以通過代碼或者配置文件來設定終結點的物理地址。
WSHttpBinding wsbinding = new WSHttpBinding();
calculatorServiceHost.AddServiceEndpoint(
typeof (ICalculator),
wsbinding,
"urn:calcservice", // 邏輯地址
new Uri ("http://localhost:8887/CalculatorService") // 物理地址
);
又或者通過配置文件來設置ListenUri,如下代碼所示:
<endpoint address="urn:calcservice"
binding ="wsHttpBinding"
contract="TerryLee.WCFAddressing.Contract.ICalculator"
listenUri="http://localhost:8887/CalculatorService"
bindingConfiguration="securityBinding">
</endpoint>
這裡我們只需要注意的是在指定物理地址時, 仍然可以使用相對地址,這一點與設置邏輯地址時是一樣的。
工作原理
現在思考一個核心的問題,當我們定義了終結點後,在WSDL中包含的是每個 終結點的邏輯地址,而非物理地址,如下代碼片段:
<wsdl:service name="CalculatorService">
<wsdl:port name="WSHttpBinding_ICalculator" binding="tns:WSHttpBinding_ICalculator">
<soap12:address location="urn:calcservice" />
<wsa10:EndpointReference>
<wsa10:Address>urn:calcservice</wsa10:Address>
</wsa10:EndpointReference>
</wsdl:port>
</wsdl:service>
如果物理地址與邏輯地址相同的,就不會 有任何問題,但是客戶端如何與一個配置了不同物理地址的服務進行交互?因為 客戶端並不關心服務端是否配置了不同的物理地址,它只知道每個終結點有一個 唯一的終結點地址,只需要跟該地址交互即可,該地址也將作為SOAP消息放在 “To”標頭中。
這時我們需要有一個特殊機制,來通知客戶 端要使用的物理地址,然後客戶端通過物理地址傳送外發消息,就如同它是路由 器或者某種類型的中介一樣,可以通過ClientViaBehavior來實現這一點,如下 代碼所示:
<system.serviceModel>
<client>
<endpoint address="urn:calcservice"
binding="wsHttpBinding"
contract="TerryLee.WCFAddressing.Contract.ICalculator"
name="defualtEndpoint"
behaviorConfiguration="calculatorEndpointBehavior"
bindingConfiguration="securityBinding">
</endpoint>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="calculatorEndpointBehavior">
<clientVia viaUri="http://localhost:8887/CalculatorService" />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
此時客戶 端將通過與服務終結點相同的物理地址 (http://localhost:8887/CalculatorService)向外傳送消息而不是通過 “urn:calcservice”,但請注意,在SOAP消息“To”標 頭中包含的仍然是邏輯地址,如圖2所示:
圖2
看到上面這幅圖,可能大家還有一個疑問,邏輯地址起什 麼作用呢?別忘了我們前面講到的消息篩選,當消息到達時, ChannelDispatcher 查詢每個相關的 EndpointDispatcher 對象以確定終結點是 否可以接受消息,以及將該消息傳遞到可以接受消息的終結點。當消息的目標地 址(To標頭中的地址)與 AddressFilter 屬性相匹配並且消息操作與 ContractFilter 屬性相匹配時,EndpointDispatcher 對象負責處理來自 ChannelDispatcher 的消息。
物理地址模式
了解了物理地址和邏輯地 址之間的關系,我們再看一下在設置監聽地址時的兩種模式,通過 ListenUriMode枚舉來指定,它定義了兩個枚舉值:
Explicit:完全原樣 使用 ListenUri,默認值。
Unique:指定傳輸是否應使用特定傳輸機制 ,以確保 ListenUri 是唯一的
根據傳輸所采用的協議不同,WCF會采用 不同的策略來保證ListenUri唯一,具體的策略如下所示:
1.非TCP傳輸 ,在ListenUri的末尾附加一個GUID。
2.對於獨占模式下的 TCP (PortSharingEnabled 為 false),綁定到一個唯一可用端口號。
3.對 於端口共享模式下的 TCP(PortSharingEnabled 為 true),在ListenUri的末 尾附加一個GUID。
TcpTrace消息截獲
前面講了這麼多物理地址和邏輯 地址,它們最重要的使用地方就是做路由。我們常用tcpTrace來做SOAP消息跟蹤 ,它正是利用這一點技術,在客戶端配置ClientViaBehavior,指向tcpTrace的 偵聽地址,然後tcpTrace在對消息做記錄後再轉發到服務端,如在服務端的配置 如下,它的物理地址和邏輯是相同的:
<service name="TerryLee.WCFAddressing.Service.CalculatorService"
behaviorConfiguration="calculatorBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8887/Calculator"/>
</baseAddresses>
</host>
<endpoint address="http://localhost:8887/CalculatorService"
binding ="wsHttpBinding"
contract="TerryLee.WCFAddressing.Contract.ICalculator"> </endpoint>
</service>
客戶端的配置, 這裡“http://localhost:8887/CalculatorService”是真正的服務 地址(邏輯地址),我們通過ClientViaBehavior告訴客戶端物理地址是 “http://localhost:8080/CalculatorService”,事實上處於該物 理地址的服務並不存在,該地址是tcpTrace的監聽地址:
<? xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:8887/CalculatorService"
binding="wsHttpBinding"
contract="TerryLee.WCFAddressing.Contract.ICalculator"
behaviorConfiguration="calculatorEndpointBehavior">
</endpoint>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="calculatorEndpointBehavior">
<clientVia viaUri="http://localhost:8080/CalculatorService" />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
tcpTrace的配置如圖3所示:
圖3
這裡tcpTrace監聽的端口號就是我們在客戶端配置的物理 地址端口號,而分發地址才是服務的真正地址,最終可以看到截獲的消息,如圖 4所示:
圖4
如果不在客戶端配置ClientViaBehavior,利用物理地址和 邏輯地址的知識,我們還可以有另外一種方式使用tcpTrace。前面我說過,邏輯 地址是包含在WSDL中,所以對於客戶端來說知道的是邏輯地址,它會向該地址發 送消息,這樣我們可以配置終結點的邏輯地址為tcpTrace偵聽的地址,而為服務 端指定另外一個物理地址,並配置tcpTrace向該物理地址轉發消息,如服務端的 配置如下:
<service name="TerryLee.WCFAddressing.Service.CalculatorService"
behaviorConfiguration="calculatorBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/Calculator"/>
</baseAddresses>
</host>
<endpoint address="http://localhost:8887/CalculatorService"
binding ="wsHttpBinding"
contract="TerryLee.WCFAddressing.Contract.ICalculator"
listenUri="http://localhost:8080/CalculatorService">
</endpoint>
</service>
而客戶端則不用再 配置ClientViaBehavior,如下代碼所示:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:8887/CalculatorService"
binding="wsHttpBinding"
contract="TerryLee.WCFAddressing.Contract.ICalculator"> </endpoint>
</client>
</system.serviceModel>
</configuration>
現在 “http://localhost:8887/CalculatorService”是邏輯地址,配置 tcpTrace監聽該地址,並向服務的物理地址 “http://localhost:8080/CalculatorService”轉發消息,如圖5所 示:
圖5
可以看到,利用物理地址和邏輯地址的知識,可以輕松的 實現路由,當然tcpTrace只是路由中非常簡單的一種使用,後面我們還會講到更 加復雜的應用。
結束語
本文詳細介紹了WCF中的物理地址和邏輯地址 ,它的相關原理以及如何使用tcpTrace來實現SOAP消息的跟蹤。