题外话:本觉得我会WCF了,精通WCF了,毕竟刚作过一个WCF的项目,不就是写写契约接口,而后实现接口,改下配置。最后用控制台或者服务发布一下,不就能用了。不就是简单ABC吗?不是So Easy吗?作第二个项目的时候我悲剧了,被碰的头破血流!突然发现什么什么都不会(第一个项目比照网上教程一步一步弄的),连写一个简单hello world都写不出来。我以前还觉得本身很懂了……html
为了避免重蹈覆辙,此次争取把他整懂整透(固然这才是入门而已)。WCF很强大,它的强大跟它的配置有很大的关系,因此我首先要先把它的配置搞懂。web
WCF的配置文件共分为两部分:服务端配置与客户端配置。二者因为功能的不一样,在配置文件的使用上也略有不一样。编程
服务端的配置文件主要包括endpoint、binding、behavior的配置。一个标准的服务端配置文件所包含的主要xml配置节以下所示:安全
<system.ServiceModel> <services> <service> <endpoint/> </service> </services> <bindings> <!—定义一个或多个系统提供的binding元素,例如<basicHttpBinding> --> <!—也能够是自定义的binding元素,如<customBinding>. --> <binding> <!—例如<BasicHttpBinding>元素. --> </binding> </bindings> <behaviors> <!—一个或多个系统提供的behavior元素. --> <behavior> <!—例如<throttling>元素. --> </behavior> </behaviors> </system.ServiceModel>
1.1 <services>配置节
在<services>配置节中能够定义多个服务,每个服务都被放到<service>配置节中,WCF的宿主程序能够经过配置文件找到这些定义的服务并发布这些服务。
<service>配置节包含name和behaviorConfiguration属性。其中,name配置了实现Service Contract的类型名。类型名必须是完整地包含了命名空间和类型名。而behaviorConfiguration的配置值则与其后的<behaviors>配置节的内容有关。<endpoint>是<service>配置节的主体,其中,<endpoint>配置节包含了endpoint的三个组成部分:Address、Binding和Contract。因为具体的binding配置是在<bindings>配置节中完成,于是,在<endpoint>中配置了bindingConfiguration属性,指向具体的binding配置。以下所示:
并发
<services>
<service name="MyService.Service1" behaviorConfiguration="MyBehavior">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="DuplexBinding" contract="MyService.IHello" />
</service>
</services>
咱们也能够定义多个endpoint,例如:
app
<services>
<service name="Microsoft.ServiceModel.Samples.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior">
<endpoint address="" binding="wsHttpBinding" contract="Microsoft.ServiceModel.Samples.ICalculator" />
<endpoint address="mex" binding="mexHttpBinding" contract=" Microsoft.ServiceModel.Samples.IMetadataExchange" />
</service>
</services>
若是address值为空,那么endpoint的地址就是默认的基地址(Base Address)。例如ICalculator服务的地址就是http://localhost/servicemodelsamples/service.svc,而IMetadataExchange服务的地址则为http://localhost/servicemodelsamples/service.svc/mex。这里所谓的基地址能够在异步
<service>中经过配置<host>来定义:
tcp
<service name="Microsoft.ServiceModel.Samples.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior"> <host> <baseAddresses> <add baseAddress="http://127.0.0.1/ServiceModelSamples”/> </baseAddresses> </host> <endpoint … /> </service>
1.2 <behaviors>配置节
当咱们在定义一个实现了Service Contract的类时, binding和address信息是客户端必须知道的,不然没法调用该服务。然而,若是须要指定服务在执行方面的相关特性时,就必须定义服务的behavior。在WCF中,定义behavior就能够设置服务的运行时属性,甚至于经过自定义behavior插入一些自定义类型。例如经过指定ServiceMetadataBehavior,可使WCF服务对外公布Metadata。配置以下:
函数
<behaviors>
<serviceBehaviors>
<behavior name="metadataSupport">
<serviceMetadata httpGetEnabled="true" httpGetUrl=""/>
</behavior>
<serviceBehaviors>
<behaviors>
在WCF中,behavior被定义为Attribute,其中,System.ServiceModel.ServiceBehaviorAttribute和System.ServiceModel.OperationBehaviorAttribute是最经常使用的behavior。虽然,behavior做为Attribute能够经过编程的方式直接施加到服务上,但出于灵活性的考虑,将behavior定义到配置文件中才是最好的设计方式。
利用ServiceBehavior与OperationBehavior能够控制服务的以下属性:
一、 对象实例的生命周期;
二、 并发与异步处理;
三、 配置行为;
四、 事务行为;
五、 序列化行为;
六、 元数据转换;
七、 会话的生命周期;
八、 地址过滤以及消息头的处理;
九、 模拟(Impersonation);
例如,经过ServiceBehavior设置对象实例的生命周期:
编码
<behaviors>
<serviceBehaviors>
<behavior name="metadataSupport">
<instanceContextMode httpGetEnabled="true" httpGetUrl=""/>
</behavior>
<serviceBehaviors>
<behaviors>
这是经过使用配置文件指定的地址
另外咱们还能够经过代码的方式指定地址:代码以下
private static void Main(string[] args) { using (ServiceHost serviceHost = new ServiceHost(typeof (Service1))) { serviceHost.AddServiceEndpoint(typeof (IService1), new WSHttpBinding(), "http://127.0.0.1:9999/Service1"); serviceHost.AddServiceEndpoint(typeof (IService1), new NetTcpBinding() , "net.tcp://127.0.0.1:8888/Service1"); serviceHost.Opened += (s, e) => Console.WriteLine("服务已打开!"); serviceHost.Open(); Console.Read(); } }
这里调用了ServiceHost 类的AddServiceEndpoint方法:它的抽象方法为
public ServiceEndPoint AddServiceEndpoint(Type implementedContract,Binding binding,string address);
固然你还可使用ServiceHost 继承自ServiceHostBase的方法AddServiceEndpoint:它的方法签名为:
public ServiceEndPoint AddServiceEndpoint(string implementedContract,Binding binding,string address);
这里只是将implementedContract以字符串的形式表示服务契约类型的有效名称。
上面添加终结地就变成了
serviceHost.AddServiceEndpoint(“命名空间.IService1”,new WSHttpBinding(),http://127.0.0.1:9999/Service1);
除了向上面那样以绝对路径的方式指定服务的终结点地址外,还能够经过“基地址+相对地址”的方式进行设置。对于一个服务来讲,能够指定一个或多个基地址,可是对于一种传输方式协议类型,只能具备一个惟一的基地址。服务的基地址与终结点相对地址能够经过编码的方式,在建立ServiceHost对象时在构造函数中指定。具体实现代码以下:
internal class Program { private static void Main(string[] args) { Uri[] baseAddress = { new Uri("http://127.0.0.1:8888/myservices"), new Uri("net.tcp://127.0.0.1/:8888") }; using (ServiceHost serviceHost = new ServiceHost(typeof (Service1),baseAddress)) { serviceHost.AddServiceEndpoint( typeof (IService1), new BasicHttpBinding(), "Service1"); serviceHost.AddServiceEndpoint( typeof (IService1), new NetTcpBinding(), "Service1"); serviceHost.Opened += (s, e) => Console.WriteLine("服务已打开!"); serviceHost.Open(); Console.Read(); } } }
上面的代码中,在寄宿Service1服务的时候,添加了两个基地址,一个是基于HTTP的,另一个是基于net.tcp的。而后为Service1添加了两个终结地,基于HTTP的BasicHttpBinding和基于TCP的NetTcpBinding。添加的两个终结点均采用相对地址Service1。
因为AddServiceEndpoint指定的是相对地址,因此WCF会根据绑定采用的传输协议在ServiceHost的基地址列表中寻找与之匹配的基地址,相对地址与基地址组合肯定终结点的绝对地址。(完整地址为:http://127.0.0.1:9999/myservices/Service1).
因为基地址与相对地址的匹配关系是根据绑定对象采用的传输协议肯定的,因此对于一个肯定的传输协议,最多只能有一个基地址。若是在上面的基地址中再加一个HTTP的基地址,那程序就会抛出异常。
若是采用上面配置文件的方式:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> <compilation debug="true" /> </system.web> <!-- 部署服务库项目时,必须将配置文件的内容添加到 主机的 app.config 文件中。System.Configuration 不支持库的配置文件。--> <system.serviceModel> <services> <service name="ConsoleApplication1.Service1"> <host> <baseAddresses> <add baseAddress = "http://127.0.0.1:9999/myservice" /> <add baseAddress="net.tcp://127.0.0.1:9999/myservice"/> </baseAddresses> </host> <!-- Service Endpoints --> <!-- 除非彻底限定,不然地址将与上面提供的基址相关 --> <endpoint address ="Service1" binding="basicHttpBinding" contract="ConsoleApplication1.IService1" bindingConfiguration="HttpStreaming"> </endpoint> <endpoint address="Service1" binding="netTcpBinding" contract="ConsoleApplication1.IService1" bindingConfiguration="netTcpBindingConfiguration"/> <!-- Metadata Endpoints --> <!-- 元数据交换终结点供相应的服务用于向客户端作自我介绍。 --> <!-- 此终结点不使用安全绑定,应在部署前确保其安全或将其删除--> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> </service> </services> <behaviors> <serviceBehaviors> <behavior> <!-- 为避免泄漏元数据信息, 请在部署前将如下值设置为 false 并删除上面的元数据终结点 --> <serviceMetadata httpGetEnabled="false"/> <!-- 要接收故障异常详细信息以进行调试, 请将如下值设置为 true。在部署前设置为 false 以免泄漏异常信息--> <serviceDebug includeExceptionDetailInFaults="False" /> <dataContractSerializer maxItemsInObjectGraph="2147483647"/> </behavior> </serviceBehaviors> </behaviors> <bindings> <basicHttpBinding> <binding name="HttpStreaming" maxReceivedMessageSize="67108864" transferMode="Streamed"/> </basicHttpBinding> <netTcpBinding> <binding name="netTcpBindingConfiguration" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="2147483647 " maxBufferSize="2147483647 " maxConnections="10" maxReceivedMessageSize="2147483647 "> <readerQuotas maxDepth="64" maxStringContentLength="2147483647 " maxArrayLength="2147483647 " maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" /> <security mode="Transport"> <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" /> </security> </binding> </netTcpBinding> </bindings> </system.serviceModel> </configuration>
注:若是采用代码和配置的方式,二者都会生效,因此必须确保二者的设置的内容不会相互冲突。
这是服务端的配置,真的搞明白了,确实还挺有意思的。不过在调试过程当中出现了错误
在服务“Service1”实现的协定列表中找不到协定名称,这个问题费我半天时间,
出错的缘由有两个:
1. 看契约是否写对, 这个通常不会写错
2.看配置文件:service name="命名空间名+服务名称" endpoint contract="命名空间名+契约名称"
(这里有个小细节要注意, ""中不能出现空格,不然依然报错)
我出的问题缘由是第二种,命名空间名前多了空格。费了半天劲原来是本身的粗枝大叶,唉,真想把本身杀了……
在编写配置中固然还出现了各类各样没法八门的问题,都是由于配置没有写对的缘由,这也给我一个教训,编写代码必定不能粗枝大叶,否则都是血的代价……
下面是本文的重点了,文件的下载。
其实WCF下载也没有什么可说的,就是写个返回Steam的接口就好了,关键就是写好配置文件就好了
(待续……)