2.3.3 ListenUri和ListenUriMode
上面介绍了终结点的ListenUri属性用于指定一个用于网络监听的物理地址,接下来讨论与ListenUri相关的另一个主题:ListenUriMode。ListenUriMode代表的是确定真正监听地址的模式。ListenUriMode通过System.ServiceModel.Description.ListenUriMode枚举表示,而ListenUriMode定义了两个枚举值:Explicit和Unique。
public enum ListenUriMode { Explicit, Unique }
ListenUriMode.Explicit表示采用终结点ListenUri属性设置的Uri作为最终的监听地址;而Unique则根据ListenUri采用不同的策略保证最终使用的监听地址是唯一的。而对于如何确保监听地址的唯一性,WCF采用如下的策略:
● 如何采用TCP作为传输协议,在不采用端口共享的情况下,会选择一个未被使用的端口作为最终监听地址的端口以确保地址的唯一性;
● 如何采用TCP作为传输协议,同时在采用端口共享情况下,会添加一个GUID作为后缀以确保地址的唯一性;
● 对于非TCP作为传输协议,会添加一个GUID作为后缀以确保地址的唯一性。
在ServiceEndpoint中,定义了一个ListenUriMode属性,用于指定终结点的ListenUriMode。
public class ServiceEndpoint { //其他成员 public Uri ListenUri { get; set; } public ListenUriMode ListenUriMode { get; set; } }
在对服务进行寄宿的时候,可以通过代码的方式为添加的终结点指定ListenUriMode。下面的代码将终结点设置成ListenUriMode.Unique。ListenUriMode也可以通过配置的方式进行指定,下面的配置和代码是等效的。
using (ServiceHost serviceHost = new ServiceHost(typeof(CalculatorService))) { ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint (typeof(ICalculator), new WSHttpBinding(), "http://127.0.0.1:9999/CalculatorService", new Uri("http://127.0.0.1:8888/CalculatorService")); endpoint.ListenUriMode = ListenUriMode.Unique; Console.Read(); } <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <service name="Artech.ListenUriDemos.Services.CalculatorService"> <endpoint address="http://127.0.0.1:9999/CalculatorService" binding="wsHttpBinding" contract="Artech.ListenUriDemos. Contracts.ICalculator" listenUriMode="Unique" listenUri="http://127.0.0.1:8888/CalculatorService" /> </service> </services> </system.serviceModel> </configuration>
案例演示:验证监听地址唯一性
为了验证ListenUriMode.Unique模式下确保监听地址的唯一性,我写了一个简单的例子。在对服务(Artech.ListenUriDemos.Services.CalculatorService)进行寄宿的时候,为之添加了5个终结点,具体的配置如表2-1所示:
表2-1
<?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. CalculatorService"> <!--1. BasicHttpBinding & ListenUriMode.Explicit--> <endpoint address="http://127.0.0.1:5555/service1" binding="basicHttpBinding" name="httpExplicitListenUriMode" contract= "Artech.ListenUriDemos.Contracts.ICalculator" /> <!--2. BasicHttpBinding & ListenUriMode.Unique--> <endpoint address="http://127.0.0.1:6666/service2" binding= "basicHttpBinding" name="httpUniquListenUriMode" contract="Artech. ListenUriDemos.Contracts.ICalculator" 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.ICalculator"/> <!--4. NetTcpBinding & ListenUriMode.Unique--> <endpoint address="net.tcp://127.0.0.1:8888/service4" binding="netTcpBinding" bindingConfiguration="" name="tcpUniquListenUriMode" contract="Artech.ListenUriDemos.Contracts.ICalculator" 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.ICalculator" listenUriMode="Unique" /> </service> </services> </system.serviceModel> </configuration>
在一个控制台应用程序中,通过下面的代码实现对服务的寄宿。当ServiceHost对象成功开启时,遍历ServiceHost的ChannelDispatcher列表,并将ChannelDispatcher对象的ChannelListener的URI打印出来。
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(CalculatorService))) { serviceHost.Open(); int i = 0; foreach (ChannelDispatcher channelDispatcher in serviceHost. ChannelDispatchers) { Console.WriteLine("第{0}个终结点的监听地址为: {1}", ++i, channelDispatcher.Listener.Uri); } Console.Read(); } } } }
最终的输出如下,从中可以看出,对于ListenUriMode.Unique的3个终结点(第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