使用命名管道 WCF 服务时通信对象出错

Posted

技术标签:

【中文标题】使用命名管道 WCF 服务时通信对象出错【英文标题】:CommunicationObjectFaulted when using a NamedPipe WCF service 【发布时间】:2012-02-14 09:34:12 【问题描述】:

我们的 .NET 应用程序使用 2 个 AppDomain。辅助域需要访问在主 appdomain 中创建的 Logger 对象。

此记录器通过具有命名管道绑定的 WCF 服务公开。

这就是我为此服务创建“客户端”的方式:

        private void InitLogger()
            
            if (loggerProxy != null)
            
                Logger.Instance.onLogEvent -= loggerProxy.Log;
            

            // Connect to the logger proxy.
            var ep = new EndpointAddress("net.pipe://localhost/app/log");
            var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);

            //Logger.Debug("Creating proxy to Logger object.");
            var channelFactory = new ChannelFactory<ILogProvider>(binding, ep);

            loggerProxy = channelFactory.CreateChannel();
            channelFactory.Faulted += (sender, args) => InitLogger();
            channelFactory.Closed += (sender, args) => InitLogger();

            Logger.Instance.onLogEvent += loggerProxy.Log;
        

最近我们收到了随机的 CommunicationObjectFaultedException - 我想这是因为通道超时或由于我失踪的其他原因而发生的。

这就是我添加 ClosedFaulted 事件处理的原因,这些事件似乎无法正常工作(也许我没有正确使用它们)。

编辑:这些事件按照建议在 Factory 对象上,所以这解释了为什么它们没有被引发。

我的问题是 -- 我怎样才能避免这些错误?

我们的场景是我们需要在应用程序的整个生命周期中一直保持这个通道打开,并且需要随时访问这个 Logger 服务,并且在任何情况下都不应该超时。

有处理这种情况的安全做法吗?

【问题讨论】:

【参考方案1】:

您的代码当前正在处理由 ChannelFactory 引发的 Closed 和 Faulted 事件,但您需要担心的是 Channel 本身的状态。

ChannelFactory 是一个人工制品,它将 WCF 服务合同的翻译封装到一个通道运行时的实例中:一旦您成功创建了您的通道 (loggerProxy),ChannelFactory 的关闭不会影响通信通过频道 - 您正在收听的事件与您的问题无关。

Channel 到 Closed 或 Faulted 的状态转换将不会被此代码注意到,结果它们将在 Logger.Instance 中显示为在调用 loggerProxy.Log 时引发的异常,并且事件您尝试登录的内容将丢失。

与其将loggerProxy.Log 直接注册为事件处理程序,不如考虑注册一个实现异常处理程序的包装函数,并围绕对loggerProxy.Log 的调用重试循环。现有通道应在异常处理程序中关闭(或者如果失败,则中止),以确保正确处理它。重试循环应该重新初始化通道并再次尝试调用。

【讨论】:

谢谢!是否有一些来自 BCL 或开源实现的类你知道已经提供了这个解决方案?【参考方案2】:

我将评论两件事 (i) 超时和 (ii) 捕获故障事件。

首先是超时。默认情况下,如果通道在默认时间段(大约 10 分钟)内没有通信,则通道会进入故障状态。您可以通过重复事件频繁地戳通道,或者将超时重置为较大的值。我做后者如下:

    NetNamedPipeBinding binding = new NetNamedPipeBinding();

    // Have to set the receive timeout to be big on BOTH SIDES of the pipe, otherwise it gets faulted and can't be used.
    binding.ReceiveTimeout = TimeSpan.MaxValue;

    DuplexChannelFactory<INodeServiceAPI> pipeFactory =
       new DuplexChannelFactory<INodeServiceAPI>(
          myCallbacks,
          binding,
          new EndpointAddress(
             "net.pipe://localhost/P2PSN.Node.Service.Pipe"));

myCallbacks 是处理双工管道中回调的类的实例,而 INodeServiceAPI 是描述我的 API 的接口。

其次,您在工厂中的事件不会被触发。您可以在频道本身上捕捉事件。我使用以下代码。

    proxy = pipeFactory.CreateChannel();
    if (proxy is IClientChannel)
    
        (proxy as IClientChannel).Faulted += new EventHandler(this.proxy_Faulted);
    

不愉快,但我从其他地方的 *** 中学到了一些有用的东西。您必须包含 System.ServiceModel 才能获取 IClientChannel 接口。

HTH。

【讨论】:

将代理端的 ReceiveTimeout 设置为 MaxValue 并不意味着发送消息和接收 NO MESSAGE 会无限期地阻塞客户端? 值得一试。我将上述内容用作 Windows 服务和管理它的 Windows 窗体应用程序之间的进程间管道的一侧。您可以不理会超时,抓住故障,取消所有内容并创建一个新频道……但这似乎很极端。当然,经常性的快速 ping 可能更适合可能尝试 DoS 的应用程序。 DoS 在这里不是问题。我正在进行应用程序域间通信。这根本不是一项服务,只是将两个应用程序域之间的方法调用粘合在一起的简单粘合剂。

以上是关于使用命名管道 WCF 服务时通信对象出错的主要内容,如果未能解决你的问题,请参考以下文章

当 2 个进程尝试同时在不同管道上相互通信时,使用命名管道的 WCF IPC 会崩溃

与命名管道和 WCF 服务的进程间通信:线程问题

使用命名管道向子进程发送参数

保护 WCF 使用的命名管道

从 VB6 到 WCF 的命名管道

WCF 命名管道最小示例