2010-09-18 125 views
6

我正在使用netNamedPipeBinding執行從Windows應用到Windows服務的進程間WCF通信。在使用回調關閉連接時收到WCF異常

現在我的應用程序在所有其他帳戶中運行良好(消除了WCF異常的公平份額,因爲任何使用WCF的人都會知道..),但是此錯誤證明是相當有彈性的。

爲了繪製我的場景圖片:我的Windows服務可以在任何給定的時間通過在Windows應用程序中按下的按鈕排隊等待一段時間,然後通過netNamedPipeBinding這是一個支持回調的綁定(兩如果您不熟悉併發起執行此項工作的請求(在本例中爲文件上傳過程),它還會從文件進度到傳輸速度等每隔幾秒鐘拋出一次回調(事件)到Windows應用程序,所以有一些相當緊密的客戶端 - 服務器集成;這就是我如何將我的Windows服務中正在運行的程序的進度返回到我的Windows應用程序中。

現在,一切都很好,除了我每次關閉應用程序(這是一個非常有效的場景)時收到的一個令人討厭的異常,WCF神對我現在都比較滿意。雖然轉移正在進行中,並且回調射擊相當嚴重,我收到此錯誤:

System.ServiceModel.ProtocolException: 
    The channel received an unexpected input message with Action 
    'http://tempuri.org/ITransferServiceContract/TransferSpeedChangedCallback' 
    while closing. You should only close your channel when you are not expecting 
    any more input messages. 

現在我明白了錯誤,但不幸的是我不能保證永遠不會收到任何更多的輸入形式交往後關閉我的頻道,如用戶可能隨時關閉該應用程序,因此該工作仍將繼續在Windows服務的後臺(類似於病毒掃描程序的運行方式)。用戶應該能夠在不受干擾的情況下儘可能多地啓動和關閉贏管理工具應用程序。

現在錯誤,我執行我的Unsubscribe()調用後立即收到錯誤,這是終止應用程序之前的第二次調用,我相信是斷開WCF客戶端的首選方式。在關閉連接之前,所有的退訂操作只是簡單地將客戶端ID從本地存儲在win服務wcf服務上的數組中刪除(因爲win服務和windows應用程序共享此實例,因爲win服務可以在預定的事件本身)和我執行客戶端ID數組刪除後,我希望(感覺)應該是一個乾淨的斷開連接。

這樣做的結果是,除了接收到一個異常,我的應用程序掛起,用戶界面完全鎖定,進度條和一切中途,所有跡象指出有競爭條件或WCF死鎖[嘆氣],但我現在很聰明,我認爲這是一個相對孤立的情況,現在閱讀例外情況,我認爲這不是一個「線索」問題,因爲它更多地指出了早期斷開連接的問題,然後把我所有的線索都打亂,也許會導致鎖定。

Unsubscribe()客戶上的做法是這樣的:

public void Unsubscribe() 
    { 
     try 
     { 
      // Close existing connections 
      if (channel != null && 
       channel.State == CommunicationState.Opened) 
      { 
       proxy.Unsubscribe(); 
      } 
     } 
     catch (Exception) 
     { 
      // This is where we receive the 'System.ServiceModel.ProtocolException'. 
     } 
     finally 
     { 
      Dispose(); 
     } 
    } 

而且我Dispose()方法,應該進行清潔斷開:

public void Dispose() 
    { 
     // Dispose object 
     if (channel != null) 
     { 
      try 
      { 
       // Close existing connections 
       Close(); 
       // Attempt dispose object 
       ((IDisposable)channel).Dispose(); 
      } 
      catch (CommunicationException) 
      { 
       channel.Abort(); 
      } 
      catch (TimeoutException) 
      { 
       channel.Abort(); 
      } 
      catch (Exception) 
      { 
       channel.Abort(); 
       throw; 
      } 
     } 
    } 

而WCF服務Subscription()對口和類屬性(供參考)在windows服務服務器(這裏沒有任何棘手,我的例外發生客戶端):

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, 
    ConcurrencyMode = ConcurrencyMode.Multiple)] 
    public class TransferService : LoggableBase, ITransferServiceContract 
    { 
     public void Unsubscribe() 
     { 
      if (clients.ContainsKey(clientName)) 
      { 
       lock (syncObj) 
       { 
        clients.Remove(clientName); 
       } 
      } 

#if DEBUG 
      Console.WriteLine(" + {0} disconnected.", clientName); 
#endif 
     } 
     ... 
    } 

接口:

[ServiceContract(
    CallbackContract = typeof(ITransferServiceCallbackContract), 
    SessionMode = SessionMode.Required)] 
public interface ITransferServiceContract 
{ 
    [OperationContract(IsInitiating = true)] 
    bool Subscribe(); 

    [OperationContract(IsOneWay = true)] 
    void Unsubscribe(); 
    ... 
} 

回調契約的接口,它不會做任何事情非常令人興奮的,只是調用通過委託事件等,我包括在此的原因是爲了展示你我的屬性。我沒有減輕一組死鎖的已經包括UseSynchronizationContext = false

[CallbackBehavior(UseSynchronizationContext = false, 
ConcurrencyMode = ConcurrencyMode.Multiple)] 
public class TransferServiceCallback : ITransferServiceCallbackContract 
{ ... } 

真的希望有人能幫幫我!非常感謝= :)

+0

我不知道具體的問題,但對於信息螺紋天翻地覆聲音*可能是由於WCF如何使用同步上下文(通過在的WinForms等形式爲)*。 – 2010-09-18 07:56:34

+0

謝謝馬克,是抓住了我,並通過閱讀這個問題緩解了一組僵局,訣竅就是在回調協議上設置UseSynchronizationContext = false;我將這個添加到我的示例中。 – GONeale 2010-09-18 08:04:56

+0

啊,對;很高興看到你已經覆蓋; p – 2010-09-18 08:10:24

回答

11

OH我的天哪,我發現了這個問題。

這個異常與解除壓制應用程序掛起無關,這只是您可以安全捕獲的預防性異常。

你不會相信它,我花了大約6個小時開啓和關閉這個bug,結果是channel.Close()鎖定等待掛起的WCF請求完成(這將永遠不會完成,直到轉移完成!which擊敗的目的!)

我剛剛去蠻力突破線後,我的問題是,如果我太慢.....它永遠不會掛,因爲不知何故渠道將可用於關閉(甚至之前轉移已經完成),所以我不得不斷開F5,然後迅速採取措施趕上掛起,這是它結束的路線。我現在只需將一個超時值應用於Close()操作,並通過TimeoutException捕獲該值,然後在通道無法及時關閉時立即中止通道!

見修復代碼:

private void Close() 
{ 
    if (channel != null && 
     channel.State == CommunicationState.Opened) 
    { 
     // If cannot cleanly close down the app in 3 seconds, 
     // channel is locked due to channel heavily in use 
     // through callbacks or the like. 
     // Throw TimeoutException 
     channel.Close(new TimeSpan(0, 0, 0, 3)); 
    } 
} 

public void Dispose() 
{ 
    // Dispose object 
    if (channel != null) 
    { 
     try 
     { 
      // Close existing connections 
      // ***************************** 
      // This is the close operation where we perform 
      //the channel close and timeout check and catch the exception. 
      Close(); 

      // Attempt dispose object 
      ((IDisposable)channel).Dispose(); 
     } 
     catch (CommunicationException) 
     { 
      channel.Abort(); 
     } 
     catch (TimeoutException) 
     { 
      channel.Abort(); 
     } 
     catch (Exception) 
     { 
      channel.Abort(); 
      throw; 
     } 
    } 
} 

我很高興有這個bug終於結束了,做用!無論當前的WCF服務狀態如何,我的應用程序現在都在3秒超時後乾淨地關閉,我希望我可以幫助其他人發現自己遭受類似問題。

格雷厄姆

+0

_這個異常與解除應用程序掛起無關,這只是一個預防性例外,你可以安全地捕獲._您是否有一個參考鏈接來說明吞嚥這種ProtocolExceptions的安全性?我有完全相同的問題。 – lesscode 2015-03-20 19:48:53

相關問題