WCF揭秘——自定义绑定

一、什么是绑定

绑定是预先配置好的信道栈,它代表了服务器与客户端之间的通信约定,每个绑定都会指定了通信所应用到的传输协调、编码等属性。在Framework3.5中已经包含basicHttpBinding、wsHttpBinding、wsDualHttpBinding、webHttpBinding、netTcpBinding、netNamedPipeBinding、netMsmqBinding、netPeerTcpBinding、msmqIntegrationBinding、wsFedrationHttpBinding、ws2007HttpBinding、ws2007FederationHttpBinding等多种绑定。其中不同的绑定支持不同的传输协议,在消息编码、传输安全、通讯方式、安全模式、可靠性会话、事务流方面有着不同的属性,能够满足大部分信息通讯的要求。

绑定类名称 传输 编码 消息版本 安全模式 可靠性会话 事务流
BasicHttpBinding HTTP 文本 SOAP 1.1 不支持 不支持
WSHttpBinding HTTP 文本 SOAP 1.2 WS-Addressing 1.0 消息 禁用 WS-AtomicTransactions
WSDualHttpBinding HTTP 文本 SOAP 1.2 WS-Addressing 1.0 消息 启用 WS-AtomicTransactions
WSFederationHttpBinding HTTP 文本 SOAP 1.2 WS-Addressing 1.0 消息 禁用 WS-AtomicTransactions
NetTcpBinding TCP 二进制 SOAP 1.2 传输 禁用 OleTransactions
NetPeerTcpBinding P2P 二进制 SOAP 1.2 传输 不支持 不支持
NetNamedPipesBinding 命名管道 二进制 SOAP 1.2 传输 不支持 OleTransactions
NetMsmqBinding MSMQ 二进制 SOAP 1.2 消息 不支持 不支持
MsmqIntegrationBinding MSMQ 不支持 不支持 传输 不支持 不支持
CustomBinding 自定义 自定义 自定义 自定义 自定义 自定义

 

二、自定义绑定元素

当预定义的绑定无法满足用户需求时,可以使用CustomBinding类开发自定义绑定,该类存在于System.ServiceModel.Channels命名空间。用户可以根据需要绑定以下属性: 事务(TransactionFlowBindingElement类)、可靠性会话(ReliableSessionBindingElement 类)、安全( SecurityBindingElement 类)、流安全、单工双工工作模式、信息编码、传输绑定等,其中信息编码和传输绑定元素是自定义绑定的必要属性,其他属性用户可根据需求制定。

  • 传输绑定元素(必要),用户可选其中一种传输绑定模式。
传输信道 传输绑定元素 绑定扩展 配置元素
TCP传输信道 TcpTransportBindingElement TcpTransportElement <tcpTransport>
HTTP传输信道 HttpTransportionBindingElement HttpTransportElement <httpTransport>
HTTPS传输信道 HttpTransportationBindingElement HttpTransportElement <httpTransport>
MSMQ传输信道 MSMQTransportBindingElement MSMQTransportElement <msmqTransport>
MSMQ集成传输信道 MSMQIntegrationBindingElement MSMQIntegrationBindingElement <msmqIntegration>
命名管道传输信道 NamedPipeTransportBindingElement NamedPipeTransportElement <namedPipeTransport>
P2P传输信道 PeerTransportBindingElement PeerTransportElement <peerTransport>
UDP传输信道 UdpTransportBindingElement UdpTransportElement <udpTransport>
  • 信息编码(必要),用户可以选择其中一种信息编码形式

1.TextMessageEncodingBindingElement,文本编码

2.BinaryMessageEncodingBindingElement,二进制编码

3.MtomMessageEncodingBindingElement,MOTM编码

  • 流安全绑定元素(可选),用户可以选择其中一种安全绑定形式

1.SslStreamSecurityBindingElement,SSL安全模式

2.WindowsStreamSecurityBindingElement,Window安全模式

  • 通信传输(可选),用户可以选择单工或双工其中一种模式

1.CompositeDuplexBindingElement,双工传输模式

2.OneWayBindingElement,单工传输模式

CustomBinding相当于一个绑定的容器,用户可以向里面加入想要的绑定元素,定制一组适合使用的绑定方式。用户可以分别使用代码、配置文件和绑定扩展类三种方式来开发自定义绑定,下面为大家一一说明。

 

三、使用代码定制自定义绑定

下面以一个最基本的自定义绑定为例子,简单说明一下如何使用代码来定制绑定,首先新建一个服务契约

 1 namespace CustomBinding.Server
 2 {
 3     // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IExampleService”。
 4     [ServiceContract(Namespace="Services")]
 5     public interface IExampleService
 6     {
 7         [OperationContract]
 8         string HelloWorld(string name);
 9     }
10   
11     public class ExampleService : IExampleService
12     {
13         public string HelloWorld(string name)
14         {
15             return "Hello " + name;
16         }
17     }
18 }

在服务器端,首先新建一个CustomBinding自定义绑定对象,加入传输绑定元素HttpTransportBindingElement,然后加入信息编码元素TextMessageEncodingBindingElement(注意,若使用HttpTransportBindingElement传输方式时,可省略信息编码绑定,那么系统将默认使用TextMessageEncodingBindingElement编码方式)。最后开放元数据,把服务行为的httpGetEnabled设置为true。

 1 namespace CustomBinding.Server
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             CustomBinding();
 8         }
 9 
10         public static void CustomBinding()
11         {
12             using (ServiceHost host = new ServiceHost(typeof(ExampleService), new Uri("http://localhost:8081/Services")))
13             {
14                 //新建一个CustomBinding对象
15                 System.ServiceModel.Channels.CustomBinding customBinding = new System.ServiceModel.Channels.CustomBinding();
16                 //设置信息编码
17                 customBinding.Elements.Add(new TextMessageEncodingBindingElement());
18                 //设置传输绑定元素
19                 customBinding.Elements.Add(new HttpTransportBindingElement());
20                 //绑定服务契约
21                 host.AddServiceEndpoint(typeof(IExampleService), customBinding, "CustomService");
22                 //开放元数据
23                 ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
24                 behavior.HttpGetEnabled = true;
25                 host.Description.Behaviors.Add(behavior);
26                 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "CustomService/mex");
27                 //启动服务
28                 host.Open();
29                 Console.WriteLine("Service Start!");
30                 Console.ReadKey();
31                 host.Close();
32             }
33         }
34     }

在客户端添加服务引用,元数据路径“http://localhost:8081/Services/CustomService/mex”,客户端将对应服务生成wsHttpBinding绑定,并使用TextMessageEncodingBindingElement文本编码元素。最后调用服务测试,若测试成功,系统将显示测试结果:“Hello Leslie”。

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <configuration>
 3     <system.serviceModel>
 4         <bindings>
 5           <!--由于自定义绑定是使用HttpTransportBindingElement绑定元素,所以客户端将自动生成wsHttpBinding设置-->
 6             <wsHttpBinding>
 7               <!--注意绑定元素将对应使用TextMessageEncodingBindingElement信息编码元素-->
 8                 <binding name="CustomBinding_IExampleService" closeTimeout="00:01:00"
 9                     openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
10                     bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
11                     maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
12                     messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
13                     allowCookies="false">
14                     <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
15                         maxBytesPerRead="4096" maxNameTableCharCount="16384" />
16                     <reliableSession ordered="true" inactivityTimeout="00:10:00"
17                         enabled="false" />
18                     <security mode="None">
19                         <transport clientCredentialType="Windows" proxyCredentialType="None"
20                             realm="" />
21                         <message clientCredentialType="Windows" negotiateServiceCredential="true" />
22                     </security>
23                 </binding>
24             </wsHttpBinding>
25         </bindings>
26         <client>
27             <endpoint address="http://localhost:8081/Services/CustomService"
28                 binding="wsHttpBinding" bindingConfiguration="CustomBinding_IExampleService"
29                 contract="ExampleService.IExampleService" name="CustomBinding_IExampleService" />
30         </client>
31     </system.serviceModel>
32 </configuration>
33 
34 namespace CustomBinding.Client
35 {
36     class Program
37     {
38         static void Main(string[] args)
39         {
40             //新建服务对象,调用服务方法
41             using (ExampleService.ExampleServiceClient example = new ExampleService.ExampleServiceClient())
42             {
43                 string data=example.HelloWorld("Leslie");
44                 Console.WriteLine(data);
45                 Console.ReadKey();
46             }
47         }
48     }
49 }

四、使用配置文件设置自定义绑定

除了使用代码设置自定义绑定以外,您也可以使用配置文件来设置自定义绑定。下面的例子,将介绍一下如何在自定义绑定中设置可靠性会话功能(关于可靠性会话的详细说明,可参考以下文章http://www.cnblogs.com/leslies2/archive/2011/08/08/2129422.html)。下面依然以上面的例子作为参考,首先在服务器端加入配置文件app.config。加入服务CustomBinding.Server.ExampleService, 绑定服务行为defaultBehavior,在服务行为中打开httpGetEnabled功能。然后把服务地址设置为http://lcoalhost:8082/CustomBinding.Server/ExampleService,加入自定义绑定CustomBinding,绑定契约CustomBinding.Server.IExampleService。最后设置自定义绑定的属性,把可靠性会话时间设置为30分钟,把信息传送方式设置为textMessageEncoding文本方式,把传输通道设置为httpTransport方式。

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <configuration>
 3   <system.serviceModel>
 4     <services>
 5       <!--根据服务契约类的命名空间设置name名称,设置服务行为defaultBehavior-->
 6       <service name="CustomBinding.Server.ExampleService" behaviorConfiguration="defaultBehavior">
 7         <host>
 8           <baseAddresses>
 9             <!--绑定服务地址-->
10             <add baseAddress="http://localhost:8082/CustomBinding.Server/ExampleService"/>
11           </baseAddresses>
12         </host>
13         <!--加入自定义绑定-->
14         <endpoint address="" contract="CustomBinding.Server.IExampleService" binding="customBinding"
15                   bindingConfiguration="customBinding"/>
16         <!--开放元数据-->
17         <endpoint address="mex" contract="IMetadataExchange" binding="mexHttpBinding"/> 
18       </service>
19     </services>
20     <behaviors>
21       <serviceBehaviors>
22         <!--设置服务行为defaultBehavior,把httpGetEnabled设置为true-->
23         <behavior name="defaultBehavior">
24           <serviceMetadata httpGetEnabled="true"/>
25         </behavior>
26       </serviceBehaviors>
27     </behaviors>
28     <bindings>
29       <customBinding>
30         <!--设置自定义绑定的属性,把信息编码方式设置为textMessageEncoding文本形式,并把传输通道设置为httpTransport-->
31         <!--把可靠性会话时间设置为30分钟-->
32         <binding name="customBinding">
33           <reliableSession inactivityTimeout="00:30:00"/>
34           <textMessageEncoding/>
35           <httpTransport/> 
36         </binding>
37       </customBinding>
38     </bindings>
39   </system.serviceModel>
40 </configuration>
设置好config文件后,启动服务
 1 namespace CustomBinding.Server
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             CustomBinding();
 8         }
 9 
10         public static void CustomBinding()
11         {
12             using(ServiceHost host=new ServiceHost(typeof(ExampleService)))
13             {
14                 Console.WriteLine("Service Start!");
15                 host.Open();
16                 Console.ReadKey();
17                 host.Close();
18             }
19         }
20     }
21 }

在客户端添加服务引用,元数据路径“http://lcoalhost:8082/CustomBinding.Server/ExampleService/mex”,客户端将对应服务生成wsHttpBinding绑定,并使用TextMessageEncoding文本编码,值得注意的是客户将对应服务器端生成30分钟可靠性会话。

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <configuration>
 3     <system.serviceModel>
 4         <bindings>
 5             <!--由于自定义绑定是使用HttpTransportBindingElement绑定元素,所以客户端将自动生成wsHttpBinding设置-->
 6             <wsHttpBinding>
 7               <!--注意绑定元素将对应使用TextMessageEncodingBindingElement信息编码元素-->
 8                 <binding name="CustomBinding_IExampleService" closeTimeout="00:01:00"
 9                     openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
10                     bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
11                     maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
12                     messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
13                     allowCookies="false">
14                     <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
15                         maxBytesPerRead="4096" maxNameTableCharCount="16384" />
16                     <!--注意客户端将对应服务器端生成30分钟的可靠性会话-->
17                     <reliableSession ordered="true" inactivityTimeout="00:30:00"
18                         enabled="true" />
19                     <security mode="None">
20                         <transport clientCredentialType="Windows" proxyCredentialType="None"
21                             realm="" />
22                         <message clientCredentialType="Windows" negotiateServiceCredential="true" />
23                     </security>
24                 </binding>
25             </wsHttpBinding>
26         </bindings>
27         <client>
28             <endpoint address="http://localhost:8082/CustomBinding.Server/ExampleService"
29                 binding="wsHttpBinding" bindingConfiguration="CustomBinding_IExampleService"
30                 contract="ExampleService.IExampleService" name="CustomBinding_IExampleService" />
31         </client>
32     </system.serviceModel>
33 </configuration>
34 
35 namespace CustomBinding.Client
36 {
37     class Program
38     {
39         static void Main(string[] args)
40         {
41             //新建服务对象,调用服务方法
42             using (ExampleService.ExampleServiceClient example = new ExampleService.ExampleServiceClient())
43             {
44                 string data=example.HelloWorld("Leslie");
45                 Console.WriteLine(data);
46                 Console.ReadKey();
47             }
48         }
49     }
50 }

五、使用绑定扩展类实现自定义绑定

使用扩展类实现自定义绑定是最灵活,使用最广泛的一种自定义绑定方式,特别适用在大型的分布式系统开发中使用,它可以根据需要实现不同的绑定类型,以适应在各种不同平台上使用。对比起前面两种方法,它的实现方式稍微复杂一些,下而为大家简单介绍一下。在MSDN上有绑定扩展类的开发实例,源代码下载http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=21459。打开项目\WF_WCF_Samples\WCF\Extensibility\Binding\NetHttpBinding,下面以此为项目例子详细讲述一下绑定扩展类的实现方式。

其中NetHttpBindingElement继承了StandardBindingElement,它暴露了配置文件中自定义绑定的可配置属性,NetHttpConfigurationStrings中设置了可配置元素的名称,NetHttpDefaults中设置了配置元素的默认值,NetHttpBindingCollectionElements提供配置节的基类,并继承了StandardBindingCollectionElement<NetHttpBinding, NetHttpBindingElement>,可以为NetHttpBindingElement中列举的属性提供预定义绑定。

 1 namespace Microsoft.Samples.NetHttpBinding {
 2 
 3     //提供配置节的基类,为NetHttpBindingElement中列举的属性提供预定义绑定
 4     public class NetHttpBindingCollectionElement : StandardBindingCollectionElement<NetHttpBinding, NetHttpBindingElement>
 5     {
 6     }
 7 
 8      //列举配置属性的名称
 9     internal class NetHttpConfigurationStrings {
10         
11         internal const string BypassProxyOnLocal = "bypassProxyOnLocal";
12         
13         internal const string HostNameComparisonMode = "hostNameComparisonMode";
14         
15         internal const string MaxBufferSize = "maxBufferSize";
16         
17         internal const string MaxBufferPoolSize = "maxBufferPoolSize";
18         
19         internal const string MaxReceivedMessageSize = "maxReceivedMessageSize";
20         
21         internal const string ProxyAddress = "proxyAddress";
22         
23         internal const string SecurityMode = "securityMode";
24         
25         internal const string TransferMode = "transferMode";
26         
27         internal const string UseDefaultWebProxy = "useDefaultWebProxy";
28         
29         internal const string ReaderQuotas = "readerQuotas";
30     }
31 
32      //设置属性的默认值
33     internal class NetHttpDefaults {
34         
35         internal const Boolean DefaultBypassProxyOnLocal = true;
36 
37         internal const HostNameComparisonMode DefaultHostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
38 
39         internal const Int32 DefaultMaxBufferSize = 65536;
40 
41         internal const Int64 DefaultMaxBufferPoolSize = 512 * 1024;
42 
43         internal const Int64 DefaultMaxReceivedMessageSize = 65536;
44 
45         internal const Uri DefaultProxyAddress = null;
46 
47         internal const NetHttpSecurityMode DefaultSecurityMode = NetHttpSecurityMode.Transport;
48 
49         internal const TransferMode DefaultTransferMode = TransferMode.Buffered;
50 
51         internal const Boolean DefaultUseDefaultWebProxy = true;
52 
53         internal const XmlDictionaryReaderQuotas DefaultReaderQuotas = null;
54     }
55     
56      //列举安全模式
57      public enum NetHttpSecurityMode
58     {
59         Transport,
60         TransportCredentialOnly,
61         None
62     }
63 }

注意若需要在通过*.config文件配置的属性,都必须在NetHttpBindingElement类中一一列举。NetHttpBindingElement必须实现InitializeFrom和OnApplyConfiguration方法,InitializeFrom方法是使用默认值初始化绑定配置元素,而OnApplyConfiguration是在绑定配置文件时调用的方法,必须实现此方法才能使配置文件的绑定属性生效。

  1 namespace Microsoft.Samples.NetHttpBinding {
  2     
  3     public class NetHttpBindingElement : StandardBindingElement {
  4         
  5         public NetHttpBindingElement(string configurationName) : 
  6                 base(configurationName) {
  7         }
  8 
  9         public NetHttpBindingElement()
 10             : 
 11                 this(null) {
 12         }
 13         
 14         protected override Type BindingElementType {
 15             get {
 16                 return typeof(NetHttpBinding);
 17             }
 18         }
 19 
 20         //利用NetHttpConfigurationStrings类设置绑定文件属性名称,利用DefaultValue类设置默认值       
            [ConfigurationProperty(NetHttpConfigurationStrings.BypassProxyOnLocal, DefaultValue = NetHttpDefaults.DefaultBypassProxyOnLocal)]
 21         public bool BypassProxyOnLocal {
 22             get {
 23                 return ((bool)(base[NetHttpConfigurationStrings.BypassProxyOnLocal]));
 24             }
 25             set {
 26                 base[NetHttpConfigurationStrings.BypassProxyOnLocal] = value;
 27             }
 28         }
 29 
 30         //利用NetHttpConfigurationStrings类设置绑定文件属性名称,利用DefaultValue类设置默认值 
           [ConfigurationProperty(NetHttpConfigurationStrings.HostNameComparisonMode, DefaultValue = NetHttpDefaults.DefaultHostNameComparisonMode)]
 31         public System.ServiceModel.HostNameComparisonMode HostNameComparisonMode {
 32             get {
 33                 return ((System.ServiceModel.HostNameComparisonMode)(base[NetHttpConfigurationStrings.HostNameComparisonMode]));
 34             }
 35             set {
 36                 base[NetHttpConfigurationStrings.HostNameComparisonMode] = value;
 37             }
 38         }
 39  
 40         //利用NetHttpConfigurationStrings类设置绑定文件属性名称,利用DefaultValue类设置默认值
            [ConfigurationProperty(NetHttpConfigurationStrings.MaxBufferSize, DefaultValue = NetHttpDefaults.DefaultMaxBufferSize)]
 41         public int MaxBufferSize {
 42             get {
 43                 return ((int)(base[NetHttpConfigurationStrings.MaxBufferSize]));
 44             }
 45             set {
 46                 base[NetHttpConfigurationStrings.MaxBufferSize] = value;
 47             }
 48         }
 49 
 50         //利用NetHttpConfigurationStrings类设置绑定文件属性名称,利用DefaultValue类设置默认值                
            [ConfigurationProperty(NetHttpConfigurationStrings.MaxBufferPoolSize, DefaultValue = NetHttpDefaults.DefaultMaxBufferPoolSize)]
 51         public long MaxBufferPoolSize {
 52             get {
 53                 return ((long)(base[NetHttpConfigurationStrings.MaxBufferPoolSize]));
 54             }
 55             set {
 56                 base[NetHttpConfigurationStrings.MaxBufferPoolSize] = value;
 57             }
 58         }
 59 
 60         //利用NetHttpConfigurationStrings类设置绑定文件属性名称,利用DefaultValue类设置默认值               
            [ConfigurationProperty(NetHttpConfigurationStrings.MaxReceivedMessageSize, DefaultValue = NetHttpDefaults.DefaultMaxReceivedMessageSize)]
 61         public long MaxReceivedMessageSize {
 62             get {
 63                 return ((long)(base[NetHttpConfigurationStrings.MaxReceivedMessageSize]));
 64             }
 65             set {
 66                 base[NetHttpConfigurationStrings.MaxReceivedMessageSize] = value;
 67             }
 68         }
 69 
 70         //利用NetHttpConfigurationStrings类设置绑定文件属性名称,利用DefaultValue类设置默认值
            [ConfigurationProperty(NetHttpConfigurationStrings.ProxyAddress, DefaultValue = NetHttpDefaults.DefaultProxyAddress)]
 71         public System.Uri ProxyAddress {
 72             get {
 73                 return ((System.Uri)(base[NetHttpConfigurationStrings.ProxyAddress]));
 74             }
 75             set {
 76                 base[NetHttpConfigurationStrings.ProxyAddress] = value;
 77             }
 78         }
 79 
 80         //利用NetHttpConfigurationStrings类设置绑定文件属性名称,利用DefaultValue类设置默认值        
 81         [ConfigurationProperty(NetHttpConfigurationStrings.SecurityMode, DefaultValue = NetHttpDefaults.DefaultSecurityMode)]
 82         public Microsoft.Samples.NetHttpBinding.NetHttpSecurityMode SecurityMode {
 83             get {
 84                 return ((Microsoft.Samples.NetHttpBinding.NetHttpSecurityMode)(base[NetHttpConfigurationStrings.SecurityMode]));
 85             }
 86             set {
 87                 base[NetHttpConfigurationStrings.SecurityMode] = value;
 88             }
 89         }
 90 
 91          //利用NetHttpConfigurationStrings类设置绑定文件属性名称,利用DefaultValue类设置默认值        
 92         [ConfigurationProperty(NetHttpConfigurationStrings.TransferMode, DefaultValue = NetHttpDefaults.DefaultTransferMode)]
 93         public System.ServiceModel.TransferMode TransferMode {
 94             get {
 95                 return ((System.ServiceModel.TransferMode)(base[NetHttpConfigurationStrings.TransferMode]));
 96             }
 97             set {
 98                 base[NetHttpConfigurationStrings.TransferMode] = value;
 99             }
100         }
101 
102        //利用NetHttpConfigurationStrings类设置绑定文件属性名称,利用DefaultValue类设置默认值                
           [ConfigurationProperty(NetHttpConfigurationStrings.UseDefaultWebProxy, DefaultValue = NetHttpDefaults.DefaultUseDefaultWebProxy)]
103         public bool UseDefaultWebProxy {
104             get {
105                 return ((bool)(base[NetHttpConfigurationStrings.UseDefaultWebProxy]));
106             }
107             set {
108                 base[NetHttpConfigurationStrings.UseDefaultWebProxy] = value;
109             }
110         }
111 
112         //利用NetHttpConfigurationStrings类设置绑定文件属性名称,利用DefaultValue类设置默认值
            [ConfigurationProperty(NetHttpConfigurationStrings.ReaderQuotas, DefaultValue = NetHttpDefaults.DefaultReaderQuotas)]
113         public System.Xml.XmlDictionaryReaderQuotas ReaderQuotas {
114             get {
115                 return ((System.Xml.XmlDictionaryReaderQuotas)(base[NetHttpConfigurationStrings.ReaderQuotas]));
116             }
117             set {
118                 base[NetHttpConfigurationStrings.ReaderQuotas] = value;
119             }
120         }
121         
122         //设置绑定属性
123         protected override ConfigurationPropertyCollection Properties {
124             get {
125                 ConfigurationPropertyCollection properties = base.Properties;
126                 properties.Add(new ConfigurationProperty(NetHttpConfigurationStrings.BypassProxyOnLocal, typeof(bool), NetHttpDefaults.DefaultBypassProxyOnLocal));
127                 properties.Add(new ConfigurationProperty(NetHttpConfigurationStrings.HostNameComparisonMode, typeof(System.ServiceModel.HostNameComparisonMode), NetHttpDefaults.DefaultHostNameComparisonMode));
128                 properties.Add(new ConfigurationProperty(NetHttpConfigurationStrings.MaxBufferSize, typeof(int), NetHttpDefaults.DefaultMaxBufferSize));
129                 properties.Add(new ConfigurationProperty(NetHttpConfigurationStrings.MaxBufferPoolSize, typeof(long), NetHttpDefaults.DefaultMaxBufferPoolSize));
130                 properties.Add(new ConfigurationProperty(NetHttpConfigurationStrings.MaxReceivedMessageSize, typeof(long), NetHttpDefaults.DefaultMaxReceivedMessageSize));
131                 properties.Add(new ConfigurationProperty(NetHttpConfigurationStrings.ProxyAddress, typeof(System.Uri), NetHttpDefaults.DefaultProxyAddress));
132                 properties.Add(new ConfigurationProperty(NetHttpConfigurationStrings.SecurityMode, typeof(Microsoft.Samples.NetHttpBinding.NetHttpSecurityMode), NetHttpDefaults.DefaultSecurityMode));
133                 properties.Add(new ConfigurationProperty(NetHttpConfigurationStrings.TransferMode, typeof(System.ServiceModel.TransferMode), NetHttpDefaults.DefaultTransferMode));
134                 properties.Add(new ConfigurationProperty(NetHttpConfigurationStrings.UseDefaultWebProxy, typeof(bool), NetHttpDefaults.DefaultUseDefaultWebProxy));
135                 properties.Add(new ConfigurationProperty(NetHttpConfigurationStrings.ReaderQuotas, typeof(System.Xml.XmlDictionaryReaderQuotas), NetHttpDefaults.DefaultReaderQuotas));
136                 return properties;
137             }
138         }
139         
140         //设置默认值
141         protected override void InitializeFrom(Binding binding) {
142             base.InitializeFrom(binding);
143             NetHttpBinding netHttpBinding = ((NetHttpBinding)(binding));
144             this.BypassProxyOnLocal = netHttpBinding.BypassProxyOnLocal;
145             this.HostNameComparisonMode = netHttpBinding.HostNameComparisonMode;
146             this.MaxBufferSize = netHttpBinding.MaxBufferSize;
147             this.MaxBufferPoolSize = netHttpBinding.MaxBufferPoolSize;
148             this.MaxReceivedMessageSize = netHttpBinding.MaxReceivedMessageSize;
149             this.ProxyAddress = netHttpBinding.ProxyAddress;
150             this.SecurityMode = netHttpBinding.SecurityMode;
151             this.TransferMode = netHttpBinding.TransferMode;
152             this.UseDefaultWebProxy = netHttpBinding.UseDefaultWebProxy;
153             this.ReaderQuotas = netHttpBinding.ReaderQuotas;
154         }
155         
156         //在绑定配置文件时发生
157         protected override void OnApplyConfiguration(Binding binding) {
158             if ((binding == null)) {
159                 throw new System.ArgumentNullException("binding");
160             }
161             if ((binding.GetType() != typeof(NetHttpBinding))) {
162                 throw new System.ArgumentException(string.Format(CultureInfo.CurrentCulture, "Invalid type for binding. Expected type: {0}. Type passed in: {1}.", typeof(NetHttpBinding).AssemblyQualifiedName, binding.GetType().AssemblyQualifiedName));
163             }
164             NetHttpBinding netHttpBinding = ((NetHttpBinding)(binding));
165             netHttpBinding.BypassProxyOnLocal = this.BypassProxyOnLocal;
166             netHttpBinding.HostNameComparisonMode = this.HostNameComparisonMode;
167             netHttpBinding.MaxBufferSize = this.MaxBufferSize;
168             netHttpBinding.MaxBufferPoolSize = this.MaxBufferPoolSize;
169             netHttpBinding.MaxReceivedMessageSize = this.MaxReceivedMessageSize;
170             netHttpBinding.ProxyAddress = this.ProxyAddress;
171             netHttpBinding.SecurityMode = this.SecurityMode;
172             netHttpBinding.TransferMode = this.TransferMode;
173             netHttpBinding.UseDefaultWebProxy = this.UseDefaultWebProxy;
174             netHttpBinding.ReaderQuotas = this.ReaderQuotas;
175         }
176     }
177 }

NetHttpBinding类是实现绑定扩展的核心,首先它实现了NetHttpBindingElement类中所包含BypassProxyOnLocal、HostNameComparisonMode、MaxBufferSize、MaxBufferPoolSize…….等等属性,这使得系统可以通过NetHttpBinding类设置绑定属性。

  1 public class NetHttpBinding: : Binding, ISecurityCapabilities
  2 {
  3         public bool BypassProxyOnLocal
  4         {
  5             get { return httpTransport.BypassProxyOnLocal; }
  6             set
  7             {
  8                 httpTransport.BypassProxyOnLocal = value;
  9                 httpsTransport.BypassProxyOnLocal = value;
 10             }
 11         }
 12 
 13         public HostNameComparisonMode HostNameComparisonMode
 14         {
 15             get { return httpTransport.HostNameComparisonMode; }
 16             set
 17             {
 18                 httpTransport.HostNameComparisonMode = value;
 19                 httpsTransport.HostNameComparisonMode = value;
 20             }
 21         }
 22 
 23         public int MaxBufferSize
 24         {
 25             get { return httpTransport.MaxBufferSize; }
 26             set
 27             {
 28                 httpTransport.MaxBufferSize = value;
 29                 httpsTransport.MaxBufferSize = value;
 30             }
 31         }
 32 
 33         public long MaxBufferPoolSize
 34         {
 35             get { return httpTransport.MaxBufferPoolSize; }
 36             set
 37             {
 38                 httpTransport.MaxBufferPoolSize = value;
 39                 httpsTransport.MaxBufferPoolSize = value;
 40             }
 41         }
 42 
 43         public long MaxReceivedMessageSize
 44         {
 45             get { return httpTransport.MaxReceivedMessageSize; }
 46             set
 47             {
 48                 httpTransport.MaxReceivedMessageSize = value;
 49                 httpsTransport.MaxReceivedMessageSize = value;
 50             }
 51         }
 52 
 53         public Uri ProxyAddress
 54         {
 55             get { return httpTransport.ProxyAddress; }
 56             set
 57             {
 58                 httpTransport.ProxyAddress = value;
 59                 httpsTransport.ProxyAddress = value;
 60             }
 61         }
 62 
 63         public NetHttpSecurityMode SecurityMode
 64         {
 65             get { return this.securityMode; }
 66             set
 67             {
 68                 if (value != NetHttpSecurityMode.Transport &&
 69                     value != NetHttpSecurityMode.TransportCredentialOnly &&
 70                     value != NetHttpSecurityMode.None)
 71                 {
 72                     throw new ArgumentOutOfRangeException("value");
 73                 }
 74                 this.securityMode = value;
 75             }
 76         }
 77 
 78         public TransferMode TransferMode
 79         {
 80             get { return httpTransport.TransferMode; }
 81             set
 82             {
 83                 httpTransport.TransferMode = value;
 84                 httpsTransport.TransferMode = value;
 85             }
 86         }
 87 
 88         public bool UseDefaultWebProxy
 89         {
 90             get { return httpTransport.UseDefaultWebProxy; }
 91             set
 92             {
 93                 httpTransport.UseDefaultWebProxy = value;
 94                 httpsTransport.UseDefaultWebProxy = value;
 95             }
 96         }
 97 
 98         public XmlDictionaryReaderQuotas ReaderQuotas
 99         {
100             get { return binaryEncoding.ReaderQuotas; }
101             set
102             {
103                 if (value != null)
104                 {
105                     value.CopyTo(binaryEncoding.ReaderQuotas);
106                 }
107             }
108         }
109 
110         public EnvelopeVersion EnvelopeVersion
111         {
112             get { return EnvelopeVersion.Soap12; }
113         }
            ........................
            ........................
114 }

其次,它继承了Binding类,Binding类中包含的CloseTimeout、MessageVersion、OpenTimeout、ReceiveTimeout…….等等属性都可以通过NetHttpBinding设置。值得注意的是,Binding类的Scheme属性必需在NetHttpBinding中实现,此属性是用于绑定通道中所使用到的传输的。而CreateBindingElements方法则是用于创建当前的绑定元素。

 1  public class NetHttpBinding : Binding, ISecurityCapabilities
 2  {
 3         HttpTransportBindingElement httpTransport;
 4         HttpsTransportBindingElement httpsTransport;
 5         BinaryMessageEncodingBindingElement binaryEncoding;
 6 
 7         NetHttpSecurityMode securityMode;
 8 
 9         public NetHttpBinding() : this(NetHttpSecurityMode.Transport)
10         {
11         }
12         public NetHttpBinding(string configurationName) : this()
13         {
14             ApplyConfiguration(configurationName);
15         }
16 
17         public NetHttpBinding(NetHttpSecurityMode securityMode)
18         {
19             if (securityMode != NetHttpSecurityMode.Transport &&
20                 securityMode != NetHttpSecurityMode.TransportCredentialOnly &&
21                 securityMode != NetHttpSecurityMode.None)
22             {
23                 throw new ArgumentOutOfRangeException("securityMode");
24             }
25 
26             this.securityMode = securityMode;   
27             this.httpTransport = new HttpTransportBindingElement();
28             this.httpsTransport = new HttpsTransportBindingElement();
29             this.binaryEncoding = new BinaryMessageEncodingBindingElement();
30         }
31 
32         //绑定通道中所用到的传输
33         public override string Scheme
34         {
35             get
36             {
37                 if (securityMode == NetHttpSecurityMode.Transport)
38                 {
39                     return httpsTransport.Scheme;
40                 }
41                 else
42                 {
43                     return httpTransport.Scheme;
44                 }
45             }
46         }
47 
48         //创建当前绑定元素
49         public override BindingElementCollection CreateBindingElements()
50         {   // return collection of BindingElements
51             BindingElementCollection bindingElements = new BindingElementCollection();
52             bindingElements.Add(binaryEncoding);
53 
54             if (this.securityMode == NetHttpSecurityMode.Transport)
55             {
56                 bindingElements.Add(this.httpsTransport);
57             }
58             else
59             {
60                 if (this.securityMode == NetHttpSecurityMode.TransportCredentialOnly)
61                 {
62                     this.httpTransport.AuthenticationScheme = AuthenticationSchemes.Negotiate;
63                 }
64                 else
65                 {
66                     this.httpTransport.AuthenticationScheme = AuthenticationSchemes.Anonymous;
67                 }
68                 bindingElements.Add(this.httpTransport);
69             }
70 
71             return bindingElements.Clone();
72         }
73         ...............
74         ...............
75 }

再次,NetHttpBinding实现了ISecurityCapabilityies接口,此接口是用于设置安全功能协议的。它包含以下几个属性,这些属性都将在NetHttpBinding中实现。

属性 说明
SupportedRequestProtectionLevel 获取绑定支持的保护级别请求。
SupportedResponseProtectionLevel 获取绑定支持的保护级别响应。
SupportsClientAuthentication 获取一个值,该值指示绑定是否支持客户端身份验证。
SupportsClientWindowsIdentity 获取一个值,该值指示绑定是否支持客户端 Windows 标识。
SupportsServerAuthentication 获取一个值,该值指示绑定是否支持服务器身份验证。

 

 1  public class NetHttpBinding : Binding, ISecurityCapabilities
 2  {
 3         System.Net.Security.ProtectionLevel ISecurityCapabilities.SupportedRequestProtectionLevel
 4         {
 5             get
 6             {
 7                 if (securityMode == NetHttpSecurityMode.Transport)
 8                 {
 9                     return ProtectionLevel.EncryptAndSign;
10                 }
11                 else
12                 {
13                     return ProtectionLevel.None;
14                 }
15             }
16         }
17 
18         System.Net.Security.ProtectionLevel ISecurityCapabilities.SupportedResponseProtectionLevel
19         {
20             get
21             {
22                 if (securityMode == NetHttpSecurityMode.Transport)
23                 {
24                     return ProtectionLevel.EncryptAndSign;
25                 }
26                 else
27                 {
28                     return ProtectionLevel.None;
29                 }
30             }
31         }
32 
33         bool ISecurityCapabilities.SupportsClientAuthentication
34         {
35             get
36             {
37                 if (securityMode == NetHttpSecurityMode.None)
38                 {
39                     return false;
40                 }
41                 else
42                 {
43                     return true;
44                 }
45             }
46         }
47 
48         bool ISecurityCapabilities.SupportsClientWindowsIdentity
49         {
50             get
51             {
52                 if (securityMode == NetHttpSecurityMode.None)
53                 {
54                     return false;
55                 }
56                 else
57                 {
58                     return true;
59                 }
60             }
61         }
62 
63         bool ISecurityCapabilities.SupportsServerAuthentication
64         {
65             get
66             {
67                 if (securityMode == NetHttpSecurityMode.None)
68                 {
69                     return false;
70                 }
71                 else
72                 {
73                     return true;
74                 }
75             }
76         }
           ...................
77 }

最后,NetHttpBinding实现 ApplyConfiguration 方法,使系统可以使用配置文件*.config来设置NetHttpBinding的属性。

 1  public class NetHttpBinding : Binding, ISecurityCapabilities
 2  {
 3         private void ApplyConfiguration(string configurationName)
 4         {
 5             BindingsSection bindings = ((BindingsSection)(ConfigurationManager.GetSection("system.serviceModel/bindings")));
 6             NetHttpBindingCollectionElement section = (NetHttpBindingCollectionElement)bindings["netHttpBinding"];
 7             NetHttpBindingElement element = section.Bindings[configurationName];
 8             if ((element == null))
 9             {
10                 throw new System.Configuration.ConfigurationErrorsException(string.Format(CultureInfo.CurrentCulture, "There is no binding named {0} at {1}.", configurationName, section.BindingName));
11             }
12             else
13             {
14                 element.ApplyConfiguration(this);
15             }
16         }
           .....................
17  }

当完成自定义绑定后,可以开始尝试在服务器端启动服务

 1 namespace Microsoft.Samples.NetHttpBinding
 2 {
 3     [ServiceContract]
 4     public interface IEchoService
 5     {
 6         [OperationContract]
 7         string Echo(string message);
 8     }
 9 
10      class Service
11     {
12         //设置服务的基础URI
13         static readonly UriBuilder uriBuilder = new UriBuilder("http://" + Environment.MachineName + ":8000/TestService");
14         static void Main(string[] args)
15         {
16             //创建自定义绑定对象
17             NetHttpBinding httpsBinding = new NetHttpBinding();
18             //设置绑定对象的安全模式
19             NetHttpBinding httpBinding = new NetHttpBinding(NetHttpSecurityMode.TransportCredentialOnly);
20             //加入服务对象 ,绑定服务地址
21             ServiceHost serviceHost = new ServiceHost(typeof(EchoService), GetBaseAddress("http", 8000), GetBaseAddress("https", 8443));
22             serviceHost.AddServiceEndpoint(typeof(IEchoService), httpBinding, "BinaryEncoderOverHTTP");
23             serviceHost.AddServiceEndpoint(typeof(IEchoService), httpsBinding, "BinaryEncoderOverHTTPS");
24             serviceHost.Open();
25 
26             Console.WriteLine("Service started at: " + serviceHost.ChannelDispatchers[0].Listener.Uri.AbsoluteUri);
27             Console.WriteLine("Service started at: " + serviceHost.ChannelDispatchers[1].Listener.Uri.AbsoluteUri);
28             Console.WriteLine("Press enter to exit...");
29             Console.ReadLine();
30         }
31         static Uri GetBaseAddress(string scheme, int port)
32         {
33             uriBuilder.Scheme = scheme;
34             uriBuilder.Port = port;
35             return uriBuilder.Uri;
36         }
37     }
38     
39     //实现服务契约
40     class EchoService : IEchoService
41     {
42         #region IEchoService Members
43 
44         public string Echo(string message)
45         {
46             Console.WriteLine("Echo called with: " + message);
47             
48             Console.WriteLine("The client is hitting endpoint: {0}", OperationContext.Current.IncomingMessageProperties.Via.AbsoluteUri);
49             ServiceSecurityContext securityContext = OperationContext.Current.ServiceSecurityContext;
50             Console.WriteLine("The client is: {0}", securityContext.IsAnonymous ? "anonymous" : securityContext.PrimaryIdentity.Name);
51             return message;
52         }
53 
54         #endregion
55     }
56 }

在MSDN中已经有此开发实例,在此也不再作详细解释。致此,已经对自定义绑定作出详细的说明,用户可以分别使用代码、配置文件和绑定扩展类三种方式来开发自定义绑定。在使用复杂的面向服务开发系统中实现自定义绑定,可以按照不同的需求实现服务,绑定不同的传输协调,文本传输方式,限制最大传输流量等,不失为实现分布式开发的一种手段。

标签