C# · 12月 25, 2021

c# – 在使用正在回调的关闭连接时收到WCF异常

我正在使用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管理工具应用程序.

现在错误,我在执行我的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服务服务器上的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{ … }

真的希望有人可以帮助我!非常感谢=