2010-09-18 65 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

相关问题