在 WCF 服务编程中,终结点是其通信的核心对象,服务通过相应的终结点发布出来,客户端通过与服务终结点匹配的终结点对服务进行调用。终结点由地址(Address)、绑定(Binding)、和契约*(Contract)三要素构成,地址在WCF的通信中既用于定位服务,也提供额外的寻址信息和进行服务认证的服务身份信息。
终结点地址的核心就是一个URL ,它作为终结点的唯一标识。URL 就是统一资源标识,它唯一地标识一个网络资源和资源所处的位置以及访问方式(访问资源所用的网络协议)。URL 的格式如下:[传输协议(Scheme)]://[主机名称|域名|IP地址] : [端口]/[资源路径]
终结点在WCF编程接口中通过System.ServiceModel.Description.ServiceEndpoint 类型表示,ServiceEndpoint 具有分别代表终结点地址、绑定和契约的三个核心属性。
1: public class ServiceEndpoint
2: {
3: public EndpointAddress Address { get; set; }
4: public Binding Binding { get; set; }
5: public ContractDescription Contract { get; set; }
6: }
服务端终结点地址
服务通过 ServiceHost 进行寄宿时,可以添加终结以暴露可被调用寻址和调用的资源,ServiceHost 类型和基类 ServiceHostBase 中定义了几个 AddServiceEndpoint 方法的重载实现不同方式添加终结点。
1: public abstract class ServiceHostBase : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable
2: {
3: public virtual void AddServiceEndpoint(ServiceEndpoint endpoint);
4: public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address);
5: public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address);
6: public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri);
7: public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri);
8: }
1: public class ServiceHost : ServiceHostBase
2: {
3: public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address);
4: public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address);
5: public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri);
6: public ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri);
7: }
通过编程方式添加服务终结点(AddServiceEndpoint)
1: using (ServiceHost host=new ServiceHost(typeof(FirstService)))
2: {
3: host.AddServiceEndpoint("Stone.Address.Contract01.IFirstContract", new NetTcpBinding(), "net.tcp://127.0.0.1/FirstService");
4: host.AddServiceEndpoint(typeof(IFirstContract),new WSHttpBinding(),"http://127.0.0.1:8085/FirstService");
5: host.Opened += (s,e) => {
6: Console.WriteLine("服务已经启动......");
7: Console.Read();
8: };
9: host.Open();
10: }
被添加到 ServiceHost 的服务终结可以通过属性 Description 获得,它是 一个ServiceDesciption 类型的属性。ServiceDesciption 表示整个服务的描述,通过服务描述可以向服务中添加行为等服务属性。
1: if (host.Description.Behaviors.Find<ServiceMetadataBehavior>()==null)
2: {
3: ServiceMetadataBehavior behavior = new ServiceMetadataBehavior() {
4: HttpGetEnabled=true,
5: HttpGetUrl=new Uri("http://127.0.0.1:8085/FirstService")
6: };
7: host.Description.Behaviors.Add(behavior);
8: }
通过配置方式配置服务终结点地址和服务行为
1: <configuration>
2: <system.serviceModel>
3: <behaviors>
4: <serviceBehaviors>
5: <behavior name="FirstServiceBehavior">
6: <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:8082/FirstService/Metadata"/>
7: </behavior>
8: </serviceBehaviors>
9: </behaviors>
10: <services>
11: <service name="Stone.Address.Service01.FirstService" behaviorConfiguration="FirstServiceBehavior">
12: <endpoint contract="Stone.Address.Contract01.IFirstContract" binding="wsHttpBinding" address="http://127.0.0.1:8082/FirstService"
13: >
14: </endpoint>
15: </service>
16: </services>
17: </system.serviceModel>
18: </configuration>
在定义服务终结点地址的时候还可以通过指定服务的“基地址+相对地址”的方式进行设置,在通过 ServiceHost 寄宿服务的时候它的构造函数定义了一个参数类型为 Uri 数组的 baseAddress,通过这个数组就可以指定服务的一组基地址,在添加服务终结点的时候就只需指定对应服务绑定的一个相对地址。WCF 在进行基地址行和相对地址匹配时,就会根据终结的绑定类型从基地址列表中获取与传输协议前缀相匹配的地址。比如:用http://和net.tcp://作为前缀的基地址就会与基于http协议绑定的BasicHttpBinding、WSHttpBinding和NetTcpBinding的绑定类型相匹配。通过配置方式进行设置时,服务基地址列表定义在<host>/<baseAddress> 配置节点下。
编程方式指定基地址:
1: Uri[] baseAddress = new Uri[] { new Uri("http://127.0.0.1:98/FirstService"), new Uri("net.tcp://127.0.0.1:99/FirstService") };
2: using (ServiceHost host=new ServiceHost(typeof(FirstService),baseAddress))
3: {
4: host.AddServiceEndpoint(typeof(IFirstContract),new WSHttpBinding(),"ColculatorService");
5: host.AddServiceEndpoint(typeof(IFirstContract), new NetTcpBinding(), "ColculatorService");
6: host.Opened += (s, e) =>
7: {
8: Console.WriteLine("服务已经启动......");
9: Console.Read();
10: };
11: host.Open();
12: }
配置方式指定基地址:
1: <service name="Stone.Address.Service01.FirstService" >
2: <endpoint contract="Stone.Address.Contract01.IFirstContract" binding="wsHttpBinding" address="ColculatorService">
3: </endpoint>
4: <endpoint contract="Stone.Address.Contract01.IFirstContract" binding="netTcpBinding" address="ColculatorService">
5: </endpoint>
6: <host>
7: <baseAddresses >
8: <add baseAddress="http://127.0.0.1:98/FirstService"/>
9: <add baseAddress="net.tcp://127.0.0.1:99/FirstService"/>
10: </baseAddresses>
11: </host>
12: </service>
服务一般是实现一个单一的契约接口,服务的所有终结点共享相同的契约。如果一个服务实现了两个不同的契约接口, 我们可以在设置服务终结点地址的时候通过对两个终结点指定相同的绑定来实现地址的跨终结点共享。
1: using (ServiceHost host=new ServiceHost(typeof(ShareEndpointService)))
2: {
3: WSHttpBinding binding = new WSHttpBinding();
4: host.AddServiceEndpoint(typeof(IFirstContract), binding, "http://localhost:92/ShareEndpointService");
5: host.AddServiceEndpoint(typeof(ISecondContract), binding, "http://localhost:92/ShareEndpointService");
6: host.Opened += (s, e) =>
7: {
8: Console.WriteLine("服务已经启动......");
9: Console.Read();
10: };
11: host.Open();
12: }
客户端终结点地址
客户端通过服务代理实现对服务的调用。我们通过 ChannelFactory<TChannel> 创建服务代理,创建服务代理时可以通过其构造函数指定服务终结点地址,也可以通过配置方式指定服务终结的地址。
1: <client>
2: <endpoint address="http://127.0.0.1:8085/FirstService" binding="wsHttpBinding"
3: bindingConfiguration="WSHttpBinding_Stone.FirstService" contract="FirstService.StoneFirstService"
4: name="WSHttpBinding_Stone.FirstService">
5: </endpoint>
6: </client>
终结点地址类型 EndpointAddress 的 Uri 属性只是代表服务的逻辑地址,而物理地址对服务端来说是真正的监听地址,对客户端来说是消息真正发送的目标地址。默认情况下,物理地址和逻辑地址是统一,但有时由于网络的限制就需要实现逻辑地址和物理地址的分离。
服务端的监听地址和监听模式
对于服务端终结点的地址是一个逻辑地址,其物理地址才是真正用于请求监听的地址,我们可以通过设置终结点的 ListenUri ,但是最终的监听地址还需要取决采用的ListenUriMode。
在添加服务终结点 AddServiceEndpoint 时,我们可以采用带有Uri 类型的 ListenUri 参数的重载方法初始化终结点的 ListenUri 属性,也可以构造一个带有 Uri 类型的 ListenUri属性的ServiceEndpoint 实例初始化 终结点的 ListenUri 属性。同样 ListenUri 可以通过配置的方式进行指定,服务终结点的配置节点具有相应的 listenUri 属性。当我们设置了终结点的 ListenUri 属性后,并不意味着终结点一定会 采用这个URI 进行请求监听最终的监听地址还需要由监听模式来决定。
1: <configuration>
2: <system.serviceModel>
3: <services>
4: <service name="Stone.Address.Service.CalculatorService" >
5: <endpoint address="http://127.0.0.1:1234/CalculatorService" binding="ws2007HttpBinding" bindingConfiguration="MyBinding"
6: contract="Stone.Address.Contract01.ICalculatorInstance"
7: listenUri="http://127.0.0.1:234/CalculatorService" listenUriMode="Explicit" >
8: </endpoint>
9: </service>
10: </services>
11: <bindings>
12: <ws2007HttpBinding>
13: <binding name="MyBinding">
14: <security mode="None"></security>
15: </binding>
16: </ws2007HttpBinding>
17: </bindings>
18: </system.serviceModel>
19: </configuration>
客户端的 ClientViaBehavior 行为
客户端是通过终结点行为来实现逻辑地址和物理地址的分离。在WCF 中提供了4中类型的行为,分别是服务行为、终结点行为、契约行为和操作行为。契约行为和操作行为被定义成了特性对应的应用在类或接口和方法上,而服务行为既可以采用声明的方式应用也可以采用配置的方式应用,终结点行只能通过配置的方式应用到服务端和客户端。服务行为和终结点行为表现在配置上是通过 <system.serviceModel> 配置节点的一个 <behaviors> 子节点下的 <serviceBehaviors> 和<endpointBehaviors> 节点实现,代表具体行为是由<behavior> 节点的一个 name 属性唯一标识配置行为。服务的配置节点<service>/</service>和终结点配置节点<endpoint>/</endpoint> 均具有一个行为配置 behaviorConfiguration 的属性,分别将服务行为和终结点行为的名称设置在 behaviorConfiguration 属性上,以实现行为应用到相应的服务和终结点上。
在客户端,终结点是通过 System.ServiceModel.Description.ClientViaBehavior 类型实现逻辑地址和物理地址的分离。他实现了 System.ServiceModel.Description.IEndpointBehavior 接口,其中 ClientViaBehavior 的Uri 属性代表物理地址(消息真正发送的目标地址)。ClientViaBehavior 行为的配置节点为 <clientVia /> ,Uri 对应的配置属性为 viaUri。
1: <configuration>
2: <system.serviceModel>
3: <client>
4: <endpoint behaviorConfiguration="listenUriMode" name="CalculatorService" address="http://127.0.0.1:3721/CalculatorService" binding="wsHttpBinding"
5: contract="Stone.Address.Contract01.ICalculatorInstance" >
6: </client>
7: <behaviors>
8: <endpointBehaviors>
9: <behavior name="listenUriMode" >
10: <clientVia viaUri="http://127.0.0.1:37/CalculatorService" />
11: </behavior>
12: </endpointBehaviors>
13: </behaviors>
14: </system.serviceModel>
15: </configuration>
WCF服务学习笔记之地址就总结到这儿,有关地址的其他信息后续再补充。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。