C# 关闭正在使用回调的连接时收到WCF异常
我正在使用C# 关闭正在使用回调的连接时收到WCF异常,c#,.net,wcf,deadlock,C#,.net,Wcf,Deadlock,我正在使用netNamedPipeBinding执行从windows应用程序到windows服务的进程间WCF通信 现在,我的应用程序在所有其他帐户中都运行良好(正如任何使用过WCF的人都会知道的那样,在消除了我的WCF例外情况后…),但这个错误被证明是相当有弹性的 描绘我的场景:我的windows服务可以通过在windows应用程序中按下一个按钮在任何给定时间排队做一些工作,然后它通过netNamedPipeBinding进行对话,如果您不熟悉,这是一个支持回调(双向通信)的绑定,并启动执行此
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服务的后台继续(有点像病毒扫描程序的操作方式)。用户应能够在不受干扰的情况下,随心所欲地启动和关闭win management tool应用程序
现在,在执行Unsubscribe()之后,我立即收到错误消息
call,这是终止应用程序之前的第二个最后一个呼叫,我认为是断开WCF客户端的首选方式。在关闭连接之前,取消订阅只需从存储在win service WCF服务本地的阵列中删除客户端id即可(由于这是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;
}
}
}
以及windows服务服务器上的WCF服务Subscription()
对应项和类属性(供参考)(这里没有什么棘手的问题,我的异常发生在客户端):
接口:
[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
{ ... }
真希望有人能帮助我!非常感谢=:)哦,天哪,我发现了问题 该异常与underyling应用程序挂起无关,这只是一个预防性异常,您可以安全地捕获它 你不会相信的,我花了大约6个小时来处理这个bug,结果是
频道.Close()
锁定等待等待等待等待完成的WCF请求(在传输完成之前永远不会完成!这违背了目的!)
我只是一行接一行地强行中断,我的问题是如果我速度太慢……它永远不会挂起,因为不知何故,通道可以关闭(甚至在传输完成之前)因此,我必须中断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;
}
}
}
我很高兴这个错误终于解决了!我的应用程序现在在3秒超时后完全关闭,不管当前WCF服务状态如何,我希望我能帮助其他人,他们发现自己也遇到了类似的问题
Graham我不知道具体的问题,但对于信息,线程混乱听起来可能是由于WCF如何使用同步上下文(这是winforms中的表单等)造成的.谢谢Marc,是的,这抓住了我,我通过阅读这个问题缓解了一系列僵局,诀窍是在回调合同上设置
UseSynchronizationContext=false
))我将把这个添加到我的示例中。啊,对;很高兴看到你已经把它盖好了;派普。我真的很讨厌处理wcf异常,对于这个项目我完全不喜欢!正如你所看到的,根据我不得不写的问题的大小,我必须讲述我的生活故事来解释:)这个例外与underyling应用程序挂起无关,这只是一个预防性的例外,你可以安全地抓住它。你有没有一个参考链接可以说明吞咽这样的原始异常的安全性?我有完全相同的问题。
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;
}
}
}