【发布时间】:2010-09-18 19:24:08
【问题描述】:
我正在使用netNamedPipeBinding 执行从 Windows 应用到 Windows 服务的进程间 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 客户端的首选方式。关闭连接之前的所有取消订阅操作只是从本地存储在 win 服务 wcf 服务上的数组中删除客户端 ID(因为这是一个由 win 服务和 windows 应用程序共享的实例,因为 win 服务可以在计划的事件本身),并且在我执行客户端 id 数组删除之后,我希望(感觉)应该是完全断开连接。
除了收到异常之外,结果是我的应用程序挂起,UI 完全锁定,进度条和所有内容都处于中间状态,所有迹象都表明存在竞争条件或 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;
}
}
}
Windows 服务 server 上的 WCF 服务 Subscription() 对应和类属性(供参考)(这里没什么棘手的,我的异常发生在客户端):
[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
{ ... }
真的希望有人可以帮助我!非常感谢 =:)
【问题讨论】:
-
我不知道具体问题,但对于信息,线程混乱听起来可能由于 WCF 如何使用同步上下文(这是通过 winforms 中的表单等)。
-
感谢 Marc,是的,这让我感到困惑,我通过阅读该问题缓解了一组死锁,其中的诀窍是在回调合约上设置
UseSynchronizationContext = false;) 我将把它添加到我的例子。 -
啊,对;很高兴看到你已经涵盖了它;p
-
是的。我非常讨厌处理 wcf 异常,并且完全不喜欢这个项目!正如你所看到的,根据我必须写的问题的大小,我必须讲述我的人生故事来解释它们:)