ServiceEndpoint具有一个可读可写的ListenUri属性,该属性表示服务端终结点的物理监听地址,该地址默认和终结点逻辑地址一致(即ServiceEndpoint的Uri)。对于客户端来说,请求真正发送的目标地址是服务的监听地址,默认情况下终结点的逻辑地址和监听地址是一样的。监听地址可以通过ServiceHost的AddServiceEndpoint指定。
public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address); public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address); public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri); public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri);
在实际的运行中,ServiceEndpoint的最终监听地址还要由ListenUriMode一起控制。ListenUriModel定义如下:
public enum ListenUriMode { Explicit = 0, Unique = 1, }
Explicit表示终结点的监听地址严格采用ListenUri指定设置的Uri,Unique会根据终结点采用的策略确定最终使用的监听地址。
在采用Unique的情况下,监听器地址的生成策略如下:
1.采用Tcp协议并且不采用端口共享,会选择一个未使用的端口作为保证监听地址的唯一性。
2.采用Tcp协议并且采用端口共享,会在原有的监听地址后面加一个Guid以保证监听地址的唯一性。
3.采用非Tcp协议,会在原有的监听地址后面加一个Guid以保证监听地址的唯一性。
下面代码演示服务端是如何确定终结点的监听地址,监听地址在信道分发器(ChannelDispatcher)中体现,
using System.ServiceModel; using System.ServiceModel.Description; using System.ServiceModel.Channels; namespace host { class Program { static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(mywcf.Calculator))) { ServiceEndpoint endpoint1 = host.AddServiceEndpoint(typeof(mywcf.ICalculator), new WSHttpBinding(), "http://localhost:1111"); ServiceEndpoint endpoint2 = host.AddServiceEndpoint(typeof(mywcf.ICalculator), new WSHttpBinding(), "http://localhost:2222"); endpoint2.ListenUriMode = ListenUriMode.Unique; ServiceEndpoint endpoint3 = host.AddServiceEndpoint(typeof(mywcf.ICalculator), new NetTcpBinding(), "net.tcp://localhost:3333"); endpoint3.ListenUriMode = ListenUriMode.Unique; NetTcpBinding tcpbind = new NetTcpBinding(); tcpbind.PortSharingEnabled = true; ServiceEndpoint endpoint4 = host.AddServiceEndpoint(typeof(mywcf.ICalculator), tcpbind, "net.tcp://localhost:4444"); endpoint4.ListenUriMode = ListenUriMode.Unique; ServiceEndpoint endpoint5 = host.AddServiceEndpoint(typeof(mywcf.ICalculator), new NetTcpBinding(), "net.tcp://localhost:5555"); host.Opened += delegate { Console.WriteLine("Service Start!"); }; host.Open(); foreach (var dispatch in host.ChannelDispatchers) { Console.WriteLine(dispatch.Listener.Uri); } } } } }
输出:
endpoint1的监听地址采用默认值,与逻辑地址一致。endpoint2使用了Unique模式,并且是非tcp协议类型,在默认的前提下添加一个GUID。endpoint3使用了Unique模式,并且是tcp协议类型,运行是分配了一个没有被使用的端口。endpoint4采用了端口共享模式,并且是tcp协议类型,监听地址加一个GUID。endpoint5监听地址默认与逻辑地址一致。
示例代码也可以用配置文件方式演示,配置文件如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <netTcpBinding> <binding portSharingEnabled="True" name="tcpbind"></binding> </netTcpBinding> </bindings> <services> <service name="mywcf.Calculator"> <endpoint contract="mywcf.ICalculator" binding="wsHttpBinding" address="http://localhost:1111"></endpoint> <endpoint contract="mywcf.ICalculator" binding="wsHttpBinding" address="http://localhost:2222" listenUriMode="Unique"></endpoint> <endpoint contract="mywcf.ICalculator" binding="netTcpBinding" address="net.tcp://localhost:3333" ></endpoint> <endpoint contract="mywcf.ICalculator" binding="netTcpBinding" address="net.tcp://localhost:4444" listenUriMode="Unique"></endpoint> <endpoint contract="mywcf.ICalculator" binding="netTcpBinding" bindingConfiguration="tcpbind" address="net.tcp://localhost:5555" listenUriMode="Unique" ></endpoint> </service> </services> </system.serviceModel> </configuration>
采用了配置文件后服务端代码就很简单了,如下:
using System.ServiceModel; namespace hostxml { class Program { static void Main(string[] args) { using (ServiceHost host = new ServiceHost(typeof(mywcf.Calculator))) { host.Opened += delegate { Console.WriteLine("Service Start!"); }; host.Open(); foreach (var dispatch in host.ChannelDispatchers) { Console.WriteLine(dispatch.Listener.Uri); } } } } }