WCF配置文件與文件下載之坎坷路

題外話:本覺得我會WCF了,精通WCF了,畢竟剛作過一個WCF的項目,不就是寫寫契約接口,而後實現接口,改下配置。最後用控制檯或者服務發佈一下,不就能用了。不就是簡單ABC嗎?不是So Easy嗎?作第二個項目的時候我悲劇了,被碰的頭破血流!突然發現什麼什麼都不會(第一個項目比照網上教程一步一步弄的),連寫一個簡單hello world都寫不出來。我以前還覺得本身很懂了……html

1、WCF文件配置

      爲了避免重蹈覆轍,此次爭取把他整懂整透(固然這纔是入門而已)。WCF很強大,它的強大跟它的配置有很大的關係,因此我首先要先把它的配置搞懂。web

     WCF的配置文件共分爲兩部分:服務端配置與客戶端配置。二者因爲功能的不一樣,在配置文件的使用上也略有不一樣。編程

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="命名空間名+契約名稱"

(這裏有個小細節要注意, ""中不能出現空格,不然依然報錯)

     我出的問題緣由是第二種,命名空間名前多了空格。費了半天勁原來是本身的粗枝大葉,唉,真想把本身殺了……

    在編寫配置中固然還出現了各類各樣沒法八門的問題,都是由於配置沒有寫對的緣由,這也給我一個教訓,編寫代碼必定不能粗枝大葉,否則都是血的代價……

    下面是本文的重點了,文件的下載。

2、WCF文件的下載

  其實WCF下載也沒有什麼可說的,就是寫個返回Steam的接口就好了,關鍵就是寫好配置文件就好了

(待續……)

相關文章
相關標籤/搜索