共用方式為


WCF 疑難排解快速入門

本主題列出客戶在開發 WCF 用戶端和服務時會碰到的幾個已知問題。如果您遇到的問題不在此清單中,建議您為您的服務設定追蹤。這會產生一個追蹤檔案,您可以使用追蹤檔案檢視器檢視這個檔案,並取得服務中可能會發生之例外狀況的詳細資訊。如需設定追蹤的詳細資訊,請參閱:設定追蹤。如需追蹤檔案檢視器的詳細資訊,請參閱:服務追蹤檢視器工具 (SvcTraceViewer.exe)

  1. 如果我的用戶端在第一個要求之後閒置一陣子,有時我會在第二個要求收到 MessageSecurityException。這是為什麼?

  2. 我的服務在與大約 10 個用戶端互動之後,開始拒絕新的用戶端。這是為什麼?

  3. 我可以從 WCF 應用程式的組態檔以外的地方載入我的服務組態嗎?

  4. 我的服務和用戶端運作良好,但是當用戶端在另一台電腦上時,它們就無法運作。這是為什麼?

  5. 當我擲回型別為例外狀況的 FaultException<Exception> 時,總是在用戶端上收到一般的 FaultException 型別而非泛型型別。這是為什麼?

  6. 當回覆未包含任何資料時,單向和要求與回覆作業似乎會以大約相同的速度傳回。這是為什麼?

  7. 我使用 X.509 憑證搭配我的服務,然後得到 System.Security.Cryptography.CryptographicException。這是為什麼?

  8. 我將作業的第一個參數從大寫變更為小寫;現在我的用戶端擲回了例外狀況。這是為什麼?

  9. 我正在使用我的其中一種追蹤工具,而我得到 EndpointNotFoundException。這是為什麼?

What is the base address? How does it relate to an endpoint address?

如果我的用戶端在第一個要求之後閒置一陣子,有時我會在第二個要求收到 MessageSecurityException。這是為什麼?

第二個要求會失敗的主要原因有兩個:(1) 工作階段已逾時或 (2) 主控服務的 Web 伺服器已回收。第一種情況中,在服務逾時之前工作階段都是有效的。當服務未在服務繫結中指定的時間內收到來自用戶端的要求時 (ReceiveTimeout),服務會終止安全性工作階段。後續的用戶端訊息會造成 MessageSecurityException。用戶端必須以此服務重新建立安全工作階段,以傳送未來的訊息或使用可設定狀態的安全性內容權杖。可設定狀態的安全性內容權杖也允許安全工作階段存留已回收的 Web 伺服器。如需詳細資訊 在安全工作階段中使用可設定狀態的安全性內容權杖,請參閱 HOW TO:為安全工作階段建立安全性內容權杖。或者,您可以停用安全工作階段。當您使用 <wsHttpBinding> 繫結時,可以將 establishSecurityContext 屬性設定為 false 以停用安全工作階段。如果要為其他繫結停用安全工作階段,必須建立自訂繫結。如需有關建立自訂繫結的詳細資料,請參閱 HOW TO:使用 SecurityBindingElement 建立自訂繫結。在您套用其中任何一種選項之前,必須了解您應用程式的安全性需求。

我的服務在與大約 10 個用戶端互動之後,開始拒絕新的用戶端。這是為什麼?

根據預設,服務同時只能有 10 個工作階段。因此,如果服務繫結使用工作階段,服務會接受新用戶端連線直到到達該數目,然後拒絕新用戶端連線,直到其中一個目前工作階段結束為止。您可以使用許多種方法來支援多個用戶端。如果您的服務不需要工作階段,請勿使用工作階段繫結 (如需詳細資訊,請參閱 使用工作階段.)另一種選擇是將 MaxConcurrentSessions 屬性的值變更為適合您情況的數目,以增加工作階段限制。

我可以從 WCF 應用程式的組態檔以外的地方載入我的服務組態嗎?

可以,不過您必須建立覆寫 ApplyConfiguration 方法的自訂 ServiceHost 類別。在該方法中,您可以呼叫基底先載入組態 (如果您也想要載入標準組態資訊),但您也可以完全取代組態載入系統。請注意,如果您要從與應用程式組態檔不同的組態檔載入組態,必須自行剖析組態檔並載入組態。

下列程式碼範例將示範如何覆寫 ApplyConfiguration 方法,以及直接設定端點。

public class MyServiceHost : ServiceHost
{
  public MyServiceHost(Type serviceType, params Uri[] baseAddresses)  
    : base(serviceType, baseAddresses)
  { Console.WriteLine("MyServiceHost Constructor"); }

  protected override void ApplyConfiguration()
  {
    string straddress = GetAddress();
    Uri address = new Uri(straddress);
    Binding binding = GetBinding();
    base.AddServiceEndpoint(typeof(IData), binding, address);
  }

  string GetAddress()
  { return "http://MyMachine:7777/MyEndpointAddress/"; }

  Binding GetBinding()
  {
    WSHttpBinding binding = new WSHttpBinding();
    binding.Security.Mode = SecurityMode.None;
    return binding;
  }
}

我的服務和用戶端運作良好,但是當用戶端在另一台電腦上時,它們就無法運作。這是為什麼?

根據例外狀況,可能有幾個問題:

  • 您可能需要將用戶端端點位址變更為主機名稱而非 "localhost"。

  • 您可能需要對應用程式開放連接埠。如需詳細資料,請參閱 SDK 範例的防火牆指示

  • 有關其他可能的問題,請參閱範例主題Running the Samples in a Workgroup and Across Machines

  • 如果您的用戶端是使用 Windows 認證,且例外狀況為 SecurityNegotiationException,請設定 Kerberos 如下。

    1. 將身分識別認證新增至用戶端的 App.config 檔案中的端點項目:

      <endpoint 
        address="http://MyServer:8000/MyService/" 
        binding="wsHttpBinding" 
        bindingConfiguration="WSHttpBinding_IServiceExample" 
        contract="IServiceExample" 
        behaviorConfiguration="ClientCredBehavior" 
        name="WSHttpBinding_IServiceExample">
        <identity>
          <userPrincipalName value="name@corp.contoso.com"/>
        </identity>
      </endpoint>
      
    2. 在 System 或 NetworkService 帳戶下執行自我主控的服務。您可以執行這個命令,在 System 帳戶下建立命令視窗:

      at 12:36  /interactive "cmd.exe"
      
    3. 在網際網路資訊服務 (IIS) 下主控服務,根據預設,它會使用服務主要名稱 (SPN) 帳戶。

    4. 使用 SetSPN 向網域註冊新的 SPN。請注意,您必須是網域系統管理員才能執行這項操作。

如需詳細資訊 Kerberos 通訊協定,請參閱用於 WCF 的安全性概念及:

當我擲回型別為例外狀況的 FaultException<Exception> 時,總是在用戶端上收到一般的 FaultException 型別而非泛型型別。結果如何呢?

強烈建議您建議自己的自訂錯誤資料型別,並宣告為您的錯誤合約中的詳細型別。原因是使用系統提供的例外狀況類型:

  • 建立類型依存性,移除服務導向應用程式的其中一個最大的強度。

  • 無法依存以標準方式序列化的例外狀況。有些 (例如 SecurityException) 可能完全不能序列化。

  • 對用戶端公開內部實作詳細資料。如需詳細資訊,請參閱 指定與處理合約和服務中的錯誤.

然而,如果您是對應用程式進行偵錯,可以使用 ServiceDebugBehavior 類別來序列化例外狀況資訊,並傳回用戶端。

當回覆未包含任何資料時,單向和要求與回覆作業似乎會以大約相同的速度傳回。這是為什麼?

將作業指定為單向只是表示作業合約接受輸入訊息,而沒有傳回輸出訊息。在 WCF 中,當傳出資料已寫入網路或擲回例外狀況時,所有用戶端叫用都會傳回。單向作業的運作方式相同,如果找不到服務可以擲回,或如果服務尚未準備好從網路接受資料便會封鎖。通常在 WCF 中,這會造成單向呼叫比要求與回覆更快傳回用戶端;但是任何減慢傳出資料在網路上的傳送速度的情況都會減慢單向作業以及要求與回覆作業。如需詳細資訊,請參閱One-Way Services使用用戶端存取服務

我使用 X.509 憑證搭配我的服務,然後得到 System.Security.Cryptography.CryptographicException。結果如何呢?

這常常發生在變更用來執行 IIS 背景工作處理序的使用者帳戶之後。例如,在 Windows XP 中,如果您將用於執行 Aspnet_wp.exe 的預設使用者帳戶從 ASPNET 變更為自訂使用者帳戶,您可能會看到這個錯誤。如果使用私密金鑰,使用它的處理序將會需要有權限才能存取儲存該金鑰的檔案。

如果是這種情況,您必須提供存取權限給處理序的帳戶,檔案才能包含私密金鑰。例如,如果 IIS 背景工作處理序正在 Bob 帳戶下執行,則您會需要為 Bob 提供含有私密金鑰的檔案的讀取權。

如需詳細資訊如何提供正確的使用者帳戶存取權給含有特定 X.509 憑證的私密金鑰的檔案,請參閱 HOW TO:讓 WCF 能夠存取 X.509 憑證

我將作業的第一個參數從大寫變更為小寫;現在我的用戶端擲回了例外狀況。這是為什麼?

作業簽章中參數名稱的值是合約的一部分,而且必須區分大小寫。當您需要區分本機參數名稱和描述用戶端應用程式的作業的中繼資料時,請使用 System.ServiceModel.MessageParameterAttribute 屬性。

我正在使用我的其中一種追蹤工具,而我得到 EndpointNotFoundException。結果如何呢?

如果您使用的追蹤工具不是系統提供的 WCF 追蹤機制,且收到 EndpointNotFoundException 指出有位址篩選不符的情況,則您需要使用 ClientViaBehavior 類別將訊息導向追蹤公用程式,然後讓公用程式將這些訊息重新導向至服務位址。ClientViaBehavior 類別會變更 Via 位址標頭,以指定與最終接收者不同的下一個網路位址,由 To 位址標頭指示。然而,執行這項操作時,請勿變更端點位址,它是用於建立 To 值的。

下列程式碼範例顯示用戶端組態檔範例。

<endpoint 
  address=https://localhost:8000/MyServer/
  binding="wsHttpBinding"
  bindingConfiguration="WSHttpBinding_IMyContract"
  behaviorConfiguration="MyClient" 
  contract="IMyContract" 
  name="WSHttpBinding_IMyContract">
</endpoint>
<behaviors>
  <endpointBehaviors>
    <behavior name="MyClient">
      <clientVia viaUri="https://localhost:8001/MyServer/"/>
    </behavior>
  </endpointBehaviors>
</behaviors>

什麼是基底位址?它與端點位址如何產生關聯?

基底位址是 ServiceHost 類別的根位址。根據預設,如果您將 ServiceMetadataBehavior 類別新增至服務組態中,會從 HTTP 基底位址擷取主機發行的所有端點的 Web 服務描述語言 (WSDL),加上提供給中繼資料行為的相對位址,加上 "?wsdl"。如果您熟悉 ASP.NET 和 IIS,基底位址就等同虛擬目錄。

使用 NetTcpBinding 在服務端點與 MEX 端點之間共用通訊埠

如果您將服務的基底位址指定為 net.tcp://MyServer:8080/MyService,並且加入下列端點:

<services>
      <service name="Microsoft.Samples.NetTcp.CalculatorService">
        <endpoint address="calcsvc" binding ="netTcpBinding" contract="Microsoft.Samples.NetTcp.ICalculator"/>
        <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
      </service>
    </services>

而且,如果您修改其中一個 NetTcpBinding 設定,如下列組態片段所示:

<bindings>
      <netTcpBinding>
        <binding closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="11" maxReceivedMessageSize="65536">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" 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.AddressAlreadyInUseException: IP 端點 0.0.0.0:9000 已有接聽項。您可以使用 MEX 端點的不同通訊埠來指定完整 URL,藉以解決這個錯誤,如下列組態片段所示:

<services>
      <service name="Microsoft.Samples.NetTcp.CalculatorService">
        <endpoint address="calcsvc" binding ="netTcpBinding" contract="Microsoft.Samples.NetTcp.ICalculator"/>
        <endpoint address="net.tcp://localhost:9001/servicemodelsamples/mex" binding="mexTcpBinding" contract="IMetadataExchange" />
      </service>
    </services>

另請參閱

概念

偵錯 Windows 驗證錯誤