前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WCF学习笔记 5

WCF学习笔记 5

作者头像
小蜜蜂
发布2019-09-03 18:10:30
6840
发布2019-09-03 18:10:30
举报
文章被收录于专栏:明丰随笔明丰随笔

WCF的宿主环境ServiceHost

服务通过 ServiceHost 进行寄宿。可以添加终结以暴露可被调用寻址和调用的资源。

ServiceHost 类型和基类 ServiceHostBase 中定义了几个 AddServiceEndpoint 方法的重载实现不同方式添加终结点:

代码语言:javascript
复制
public abstract class ServiceHostBase : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable
{
    public virtual void AddServiceEndpoint(ServiceEndpoint endpoint);
    public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address);
    public ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address);
    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);
    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);
}

WCF的ServiceEndpoint

在 WCF 服务编程中,终结点是其通信的核心对象,服务通过相应的终结点发布出来,客户端通过与服务终结点匹配的终结点对服务进行调用。

终结点由地址(Address)、绑定(Binding)、和契约(Contract)三要素构成,地址在WCF的通信中既用于定位服务,也提供额外的寻址信息和进行服务认证的服务身份信息。

终结点在WCF编程接口中通过 ServiceEndpoint 类型表示,ServiceEndpoint 具有分别代表终结点地址、绑定和契约的三个核心属性:

代码语言:javascript
复制
namespace System.ServiceModel.Description
public class ServiceEndpoint
{
    public EndpointAddress Address { get; set; }
    public Binding Binding { get; set; }
    public ContractDescription Contract { get; set; }
}

地址(EndpointAddress)的核心就是一个URL ,它作为终结点的唯一标识。URL 就是统一资源标识,它唯一地标识一个网络资源和资源所处的位置以及访问方式(访问资源所用的网络协议)。

通过编程方式添加服务终结点(AddServiceEndpoint):

代码语言:javascript
复制
using (ServiceHost host = new ServiceHost(typeof(FirstService)))
{
    host.AddServiceEndpoint("Stone.Address.Contract01.IFirstContract", new NetTcpBinding(), "net.tcp://127.0.0.1/FirstService");
    host.AddServiceEndpoint(typeof(IFirstContract), new WSHttpBinding(), "http://127.0.0.1:8085/FirstService");
    host.Opened += (s,e) => {
        Console.WriteLine("服务已经启动......");
        Console.Read();
    };
    host.Open();
}

通过配置添加服务终结点:

代码语言:javascript
复制
<configuration>
  <system.serviceModel>
    <services>
      <service name="Stone.Address.Service01.FirstService" behaviorConfiguration="FirstServiceBehavior">
        <endpoint contract="Stone.Address.Contract01.IFirstContract" binding="wsHttpBinding" address="http://127.0.0.1:8082/FirstService">
        </endpoint>
      </service>
    </services>
  </system.serviceModel>
</configuration>

被添加到 ServiceHost 的服务终结可以通过属性 Description 获得,它是 一个ServiceDesciption 类型的属性.

ServiceDesciption类表示着具体的一个服务信息,包括:

服务类型

名称

命名空间

约定名称

Behaviors

服务终结点(Endpoints)

下面列出这个类的主要成员:

代码语言:javascript
复制
public class ServiceDescription
{
  /// <summary>Gets the behaviors associated with the service.</summary>
  /// <returns>The <see cref="T:System.Collections.Generic.KeyedByTypeCollection`1" /> of type <see cref="T:System.ServiceModel.Description.IServiceBehavior" /> that contains the behaviors associated with the service.</returns>
  public KeyedByTypeCollection<IServiceBehavior> Behaviors
  /// <summary>Gets or sets the name of the &lt;service&gt; configuration element.</summary>
  /// <returns>The name of the &lt;service&gt; configuration element used to configure the service.</returns>
  public string ConfigurationName
  /// <summary>Gets the collection of endpoints from the service description.</summary>
  /// <returns>The <see cref="T:System.ServiceModel.Description.ServiceEndpointCollection" /> that contains the endpoints defined for the service.</returns>
  public ServiceEndpointCollection Endpoints
  /// <summary>Gets or sets the name of the service.</summary>
  /// <returns>The name of the service.</returns>
  public string Name
  /// <summary>Gets or sets the namespace for the service.</summary>
  /// <returns>The namespace for the service.</returns>
  public string Namespace
  /// <summary>Gets the type of the service.</summary>
  /// <returns>An instance of the <see cref="T:System.Type" /> that implements the service.</returns>
  public Type ServiceType

  /// <summary>Initializes a new instance of the <see cref="T:System.ServiceModel.Description.ServiceDescription" /> class from a specified enumeration of service endpoints.</summary>
  /// <param name="endpoints">The <see cref="T:System.Collections.Generic.IEnumerable`1" /> of type <see cref="T:System.ServiceModel.Description.ServiceEndpoint" /> used to initialize the service description.</param>
  /// <exception cref="T:System.ArgumentNullException">
  ///   <paramref name="endpoints" /> is null.</exception>
  public ServiceDescription(IEnumerable<ServiceEndpoint> endpoints) : this()
  {
    ...
  }

  /// <summary>Returns a service description initialized with a specified service type.</summary>
  /// <returns>The <see cref="T:System.ServiceModel.Description.ServiceDescription" /> for the service type provided.</returns>
  /// <param name="serviceType">The <see cref="T:System.Type" /> of the service.</param>
  /// <exception cref="T:System.ArgumentNullException">
  ///   <paramref name="serviceType" /> is null.</exception>
  public static ServiceDescription GetService(Type serviceType)
  {
    ...
  }

  /// <summary>Returns a service description initialized with a specified service object.</summary>
  /// <returns>The <see cref="T:System.ServiceModel.Description.ServiceDescription" /> for the service object provided.</returns>
  /// <param name="serviceImplementation">The <see cref="T:System.Object" /> that implements the service.</param>
  /// <exception cref="T:System.ArgumentNullException">
  ///   <paramref name="serviceImplementation" /> is null.</exception>
  public static ServiceDescription GetService(object serviceImplementation)
  {
    ...
  }
}

通过服务描述(ServiceDesciption)可以向服务中添加行为(Behavior)等服务属性:

代码语言:javascript
复制
if (host.Description.Behaviors.Find<ServiceMetadataBehavior>()==null)
{
    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior() { 
    HttpGetEnabled=true,
    HttpGetUrl=new Uri("http://127.0.0.1:8085/FirstService")
    };
    host.Description.Behaviors.Add(behavior);
}

通过配置方式添加行为(Behavior):

代码语言:javascript
复制
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="FirstServiceBehavior">
          <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:8082/FirstService/Metadata"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

在定义服务终结点地址的时候还可以通过指定服务的“基地址+相对地址”的方式进行设置,在通过 ServiceHost 寄宿服务的时候它的构造函数定义了一个参数类型为 Uri 数组的 baseAddress,通过这个数组就可以指定服务的一组基地址,在添加服务终结点的时候就只需指定对应服务绑定的一个相对地址。WCF 在进行基地址行和相对地址匹配时,就会根据终结的绑定类型从基地址列表中获取与传输协议前缀相匹配的地址。

比如:用http://和net.tcp://作为前缀的基地址就会与基于http协议绑定的BasicHttpBinding、WSHttpBinding和NetTcpBinding的绑定类型相匹配。

编程方式指定基地址:

代码语言:javascript
复制
Uri[] baseAddress = new Uri[] { new Uri("http://127.0.0.1:98/FirstService"), new Uri("net.tcp://127.0.0.1:99/FirstService") };
using (ServiceHost host=new ServiceHost(typeof(FirstService),baseAddress))
{
    host.AddServiceEndpoint(typeof(IFirstContract),new WSHttpBinding(),"ColculatorService");
    host.AddServiceEndpoint(typeof(IFirstContract), new NetTcpBinding(), "ColculatorService");
    host.Opened += (s, e) =>
    {
        Console.WriteLine("服务已经启动......");
        Console.Read();
    };
    host.Open();
}

配置方式指定基地址:

代码语言:javascript
复制
<service name="Stone.Address.Service01.FirstService" >
  <host>
    <baseAddresses>
      <add baseAddress="http://127.0.0.1:98/FirstService"/>
      <add baseAddress="net.tcp://127.0.0.1:99/FirstService"/>
    </baseAddresses>
  </host>
  <endpoint contract="Stone.Address.Contract01.IFirstContract" binding="wsHttpBinding" address="ColculatorService">
  </endpoint>
  <endpoint contract="Stone.Address.Contract01.IFirstContract" binding="netTcpBinding" address="ColculatorService">
  </endpoint>
</service>

客户端终结点地址

客户端通过服务代理实现对服务的调用。我们通过 ChannelFactory<TChannel> 创建服务代理,创建服务代理时可以通过其构造函数指定服务终结点地址。

也可以通过配置方式指定服务终结的地址:

代码语言:javascript
复制
<client> 
  <endpoint 
    address="http://127.0.0.1:8085/FirstService" 
    binding="wsHttpBinding"
    bindingConfiguration="WSHttpBinding_Stone.FirstService" 
    contract="FirstService.StoneFirstService"
    name="WSHttpBinding_Stone.FirstService">
  </endpoint>
</client>

逻辑地址与物理地址的分离

终结点地址类型 EndpointAddress 的 Uri 属性只是代表服务的逻辑地址,而物理地址对服务端来说是真正的监听地址,对客户端来说是消息真正发送的目标地址。默认情况下,物理地址和逻辑地址是统一,但有时由于网络的限制就需要实现逻辑地址和物理地址的分离。

服务端的监听地址和监听模式

对于服务端终结点的地址是一个逻辑地址,其物理地址才是真正用于请求监听的地址,我们可以通过设置终结点的 ListenUri 来定义物理地址,但是最终的监听地址还需要取决采用的ListenUriMode。

在添加服务终结点 AddServiceEndpoint 时,我们可以采用带有Uri 类型的 ListenUri 参数的重载方法初始化终结点的 ListenUri 属性,也可以构造一个带有 Uri 类型的 ListenUri属性的ServiceEndpoint 实例初始化 终结点的 ListenUri 属性。同样 ListenUri 可以通过配置的方式进行指定,服务终结点的配置节点具有相应的 listenUri 属性。当我们设置了终结点的 ListenUri 属性后,并不意味着终结点一定会 采用这个URI 进行请求监听最终的监听地址还需要由监听模式来决定。

代码语言:javascript
复制
<configuration>
  <system.serviceModel>
    <services>
      <service name="Stone.Address.Service.CalculatorService" >
        <endpoint  
      address="http://127.0.0.1:1234/CalculatorService"  
      binding="ws2007HttpBinding"  
      bindingConfiguration="MyBinding"
      contract="Stone.Address.Contract01.ICalculatorInstance"
      listenUri="http://127.0.0.1:234/CalculatorService"  
      listenUriMode="Explicit" >
    </endpoint>
      </service>
    </services>
    <bindings>
      <ws2007HttpBinding>
        <binding name="MyBinding">
          <security mode="None"></security>
        </binding>
      </ws2007HttpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

客户端的 ClientViaBehavior 行为

客户端是通过终结点行为来实现逻辑地址和物理地址的分离。在WCF 中提供了4种类型的Behavior,分别是服务Behavior、终结点Behavior、契约Behavior和操作Behavior。

契约Behavior和操作Behavior被定义成了特性对应的应用在类或接口和方法上。

服务Behavior既可以采用声明的方式应用也可以采用配置的方式应用。

终结点Behavior只能通过配置的方式应用到服务端和客户端。

服务Behavior和终结点Behavior表现在配置上是通过:

代码语言:javascript
复制
<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior name="name1">
    <endpointBehaviors> 
      <behavior name="name2">
      ...

通过配置上面的节点可以控制服务Behavior和终结点Behavior,代表具体行为是由<behavior>节点的一个 name 属性唯一标识配置行为。

服务的配置节点

<service>...</service>

和终结点配置节点

<endpoint>...</endpoint>

均具有一个行为配置 behaviorConfiguration 的属性,分别将服务行为和终结点行为的名称设置在 behaviorConfiguration 属性上,以实现对应的behavior应用到相应的服务和终结点上。

在客户端,终结点是通过

System.ServiceModel.Description.ClientViaBehavior

类型实现逻辑地址和物理地址的分离。该类实现了接口:

System.ServiceModel.Description.IEndpointBehavior

其中 ClientViaBehavior 的 Uri 属性代表物理地址(消息真正发送的目标地址)。

ClientViaBehavior 行为的配置节点为 <clientVia /> ,Uri 对应的配置属性为 viaUri。

代码语言:javascript
复制
<configuration>
  <system.serviceModel>
    <client>      
      <endpoint  
        behaviorConfiguration="listenUriMode"  
        name="CalculatorService"  
        address="http://127.0.0.1:3721/CalculatorService"  
        binding="wsHttpBinding"
        contract="Stone.Address.Contract01.ICalculatorInstance" />
    </client>
    <behaviors>
      <endpointBehaviors>
        <behavior name="listenUriMode" >
          <clientVia viaUri="http://127.0.0.1:37/CalculatorService" />
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-08-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 明丰随笔 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档