none
ProtocolException的问题 RRS feed

  • 问题

  • 做了一个WCF的应用,在公司的机器上完全没问题。

    但是在客户的地方出问题饿了。

    System.ServiceModel.ProtocolException: The requested upgrade is not supported by 'net.pipe://localhost/xxxx/xxxx'. This could be due to mismatched bindings (for example security enabled on the client and not on the server).
    Server stack trace: 
       Ъϊ System.ServiceModel.Channels.ConnectionUpgradeHelper.DecodeFramingFault(ClientFramingDecoder decoder, IConnection connection, Uri via, String contentType, TimeoutHelper& timeoutHelper)
       Ъϊ System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.SendPreamble(IConnection connection, ArraySegment`1 preamble, TimeoutHelper& timeoutHelper)
       Ъϊ System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.DuplexConnectionPoolHelper.AcceptPooledConnection(IConnection connection, TimeoutHelper& timeoutHelper)
       Ъϊ System.ServiceModel.Channels.ConnectionPoolHelper.EstablishConnection(TimeSpan timeout)
       Ъϊ System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.OnOpen(TimeSpan timeout)
       Ъϊ System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
       Ъϊ System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
       Ъϊ System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
       Ъϊ System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
       Ъϊ System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
       Ъϊ System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
       Ъϊ System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
       Ъϊ System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

    Exception rethrown at [0]: 
       Ъϊ System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       Ъϊ System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       Ъϊ xxx.xxxxx.Communication.Contracts.IxxxComClientService.Register(ClientInfo clientInfo)
       Ъϊ xxx.xxxxx.Communication.xxxComClient.ActionClientRegisterOnlineClientInfo(IxxxComClientService channel)
       Ъϊ xxx.xxxxx.Communication.xxxComClient.ActionReceivedClientOnline(EndpointAddress endpoint)

    查原因,网上说是因为NetNamedPipeSecurityMode不一致。应用中我是直接使用默认的,没有做过设置,应该是Transport。

    请问,有朋友知道,这具体是因为系统环境做了什么特定的设置而导致的结果吗?



    野老

    2012年12月2日 9:41

答案

  • 调查到这里基本上结束了。

    原因出在这个 奇怪的服务 的地址指定上。

    原因1,奇怪的服务 运行在WindowsService上,以管理者权限运行的,并且访问地址指定的是net.pipe根地址,以至于所有的pipe访问都被它抓了。

    原因2,我的服务是运行在应用程序上的,仅仅是作为普通用户运行,终端访问的时候,抢不过 奇怪的服务。

    对策1,起个服务把这个外包培训学校做的东西给关了。

    对策2,把我的服务直接上升到顶层命名空间,让我的服务访问时,能够直接访问到我指定的地址。

    疑问1,为什么在win7上没有这个问题,win8上有这个问题。难道WCF在4.5上的访问机制变了?(NetPeerTCPBinding已经标记为过时,虽然没有去找变更文档,但是可以知道4.5里面肯定做了某些变化。)


    野老

    2012年12月14日 7:57

全部回复

  • 把你客户端和服务端的 配置文件贴出来 看看。问题应该就在配置上

    Frank Xu Lei--谦卑若愚,好学若饥
    [老徐的网站]:http://www.frankxulei.com/

    [老徐的博客]:http://54peixun.com/Author/frankxulei
    微软WCF中文技术论坛
    微软WCF英文技术论坛

    Windows Azure中文技术论坛

    2012年12月3日 9:04
    版主
  • 谢谢老徐。简要来说,关于WCF部分很少。我把伪代码贴上来看看吧。

    一个有自动广播及接收上线下线状态的服务端。接收到之后就会记录上线下线对方的地址信息。

    用的是NetNamedPipe。

    /**
    *一个Self-Host的服务,可以接收同类服务online/offline时的匿名广播。
    */
    class Client
    {
            private ServiceHost _announcementsListener;//用于监听广播
            private ServiceHost _clientServiceHost;//自服务
    
            public Class1()
            {
                //一堆乱七八糟的配置,其中的Address指定了net.pipe//localhost/xxx/guid
    
                _clientInstance = new ClientInfo();            
                ActionInitClientService();
                ActionInitAnnouncementsListener();
            }
    
            private void ActionInitClientService()
            {
                _clientServiceHost = new ServiceHost(_clientInstance);
                _clientServiceHost.AddServiceEndpoint((typeof(IxxxComClientService)), new NetNamedPipeBinding(), _clientInstance.Info.Address);//★开始怀疑是这里,没指定SecurityMode的时候是传输加密
    
                // 追加可被发现节点
                _clientServiceHost.AddServiceEndpoint(new UdpDiscoveryEndpoint());
                var discoveryBehavior = new ServiceDiscoveryBehavior();
                discoveryBehavior.AnnouncementEndpoints.Add(new UdpAnnouncementEndpoint());
                _clientServiceHost.Description.Behaviors.Add(discoveryBehavior);
            }
    
            private void ActionInitAnnouncementsListener()
            {
                // 追加上下线广播节点
                var announcementService = new AnnouncementService();
                announcementService.OnlineAnnouncementReceived += OnOnlineAnnouncementReceived;
                announcementService.OfflineAnnouncementReceived += OnOfflineAnnouncementReceived;
    
                _announcementsListener = new ServiceHost(announcementService);
                _announcementsListener.AddServiceEndpoint(new UdpAnnouncementEndpoint());
            }
    
            private void OnOnlineAnnouncementReceived(object sender, AnnouncementEventArgs e)
            {
                if (e.EndpointDiscoveryMetadata.ContractTypeNames.FirstOrDefault(
                    x => x.Name == typeof(IxxxComClientService).Name) != null)
                {
                    ActionReceivedClientOnline(e.EndpointDiscoveryMetadata.Address);
                }
            }
    
            private void OnOfflineAnnouncementReceived(object sender, AnnouncementEventArgs e)
            {
                if (e.EndpointDiscoveryMetadata.ContractTypeNames.FirstOrDefault(
                    x => x.Name == typeof(IxxxComClientService).Name) != null)
                {
                    ActionReceivedClientOffline(e.EndpointDiscoveryMetadata.Address);
                }
            }
    
            private void ActionReceivedClientOnline(EndpointAddress endpoint)
            {
                try
                {
                    // 过滤噪音地址
                    if (endpoint.IsAddressEqual(_clientInstance.Info)) return;
                    if (!endpoint.IsActiveEndpointAddress()) return;
    
                    // 和有效地址建立链接
                    var binding = new NetNamedPipeBinding();
                    using (var factory = new ChannelFactory<IxxxComClientService>(binding, endpoint))
                    {
                        var channel = factory.CreateChannel();
    
                        try
                        {
                            // 建立连接是两个Client相互把对方的路径信息等加入到一个列表中。
                            var addClientInfo = channel.Register(_clientInstance.Info);
                            _clientInstance.Register(addClientInfo);
                        }
                        catch (Exception ex)
                        {
                            //
                        }
                        finally
                        {
                            ActionCloseChannel((ICommunicationObject)channel);
                        }
                    }
                }
                finally
                {
                    //
                }
            }
    
            private void ActionReceivedClientOffline(EndpointAddress endpoint)
            {
                try
                {
                    if (endpoint.IsAddressEqual(_clientInstance.Info)) return;
                    if (!endpoint.IsActiveEndpointAddress()) return;
    
                    // 把下线的Client信息从自己的列表中剔除。
                    var removeClientInfo = _clientInstance.GetRegisteredClient(endpoint.Uri);
    
                    if (removeClientInfo != null)
                    {
                        _clientInstance.Unregister(removeClientInfo.Address);
                    }
                }
                finally
                {
                    //
                }
            }
    
            private static void ActionCloseChannel(ICommunicationObject channel)
            {
                try
                {
                    if (channel.InTheState(CommunicationState.Opened))
                    {
                        channel.Close();
                        channel = null;
                    }
                }
                catch (Exception ex)
                {
                }
                finally
                {
                    if (channel != null) channel.Abort();
                }
            }
        }



    野老


    2012年12月4日 0:59
  • 老徐有时间帮我看看吗?

    野老

    2012年12月5日 5:41
  • System.ServiceModel.ProtocolException: The requested upgrade is not supported by 'net.pipe://localhost/xxxx/xxxx'. This could be due to mismatched bindings (for example security enabled on the client and not on the server)

    这个

    NetNamedPipeBinding

    默认我不知道是不是启用了安全。你可以在代码里 客户端和服务 都把 SecurityMode =None。这样先去掉测试一下试试


    Frank Xu Lei--谦卑若愚,好学若饥
    [老徐的网站]:http://www.frankxulei.com/

    [老徐的博客]:http://54peixun.com/Author/frankxulei
    微软WCF中文技术论坛
    微软WCF英文技术论坛

    Windows Azure中文技术论坛

    2012年12月5日 6:24
    版主
  • 上面那个代码里面有个注释

    //★开始怀疑是这里,没指定SecurityMode的时候是传输加密

    出问题的时候我查过了,用NONE放上去之后,还是出问题。所以完全没有头绪到底是怎样了。

    出log比较了在客户处以及在公司环境下的记录之后,发现在客户那边出问题的地方,

    匿名广播ServiceHost.open之后,正常log会有一句特定的EndPointAddress被指定,但是异常log里面没有这句。

    再往下就是那个双方安全等级不一致的情况了。

    我还怀疑是用户那边直接就把网络通讯给掐断了。

    但是想想人家是店头,要用网络的。所以想不通是什么原因了。


    野老


    2012年12月6日 9:17
  • 挨个关服务,发现关掉其中某一个用户用来做远程控制的服务之后,执行就成功了。

     channel.Register(_clientInstance.Info);

    这个部分,如果这个服务启动起来的话,是无法成功的。

    Update: 最后确认了,果然是把NetNamedPipe给屏蔽掉了。

    只起来一个ServiceHost,

    A:net.pipe://localhost/PipeReverse

    B:NetNamedPipeBinding

    C:VS2010默认的WCFApplication给的IService

    Client侧,CreateChannel后,去使用C的时候,立刻

    ProtocolException

    野老


    2012年12月7日 6:52
  • 老徐有什么现行的用NetNamedPipe作为通讯的应用吗?

    野老

    2012年12月10日 7:03
  • 基本没人用WCF的 NetNamedPipe 来做通讯。

    2012年12月11日 2:27
  • 虽然我不知道基本有没有人用,但是我知道它提供了这种用法,对吧。

    而且在官方的文档上,如何选binding这个图上,也有明确的说IPC推荐使用NetNamedPipe,这个也是确有其事吧。

    要么你让微软承认这是他们的bug,公布修正方式,

    如果不是,那就是我这边的问题,

    既然是我的问题,我是想知道是为什么。


    野老

    2012年12月11日 4:08
  • 不是说这个不能用,我只是回答“老徐有什么现行的用NetNamedPipe作为通讯的应用吗?”这个问题。

    排查问题得一步一步来,

    1,首先你得保证基于 NamedPipe 的服务正确启动了;

    2,然后你的客户端能通过正常访问服务;

    3,Transport 会使用 Windows NTLM 认证;

    4,另外你与客户机的差别是不是xp和Win7的差别,如果是Win7,请尝试用管理员权限运行你的服务;

    前面3个问题需要你自己测试,得到错误代码,来一一排查,最后一个问题是有具体解决方案的,

    因为UAC,很多API在使用上会有差别,创建命名管道的API CreateNamedPip在Win7上需要将

    最后一个参数LPSECURITY_ATTRIBUTES设置为自定义的安全描述符;当然你也可以以管理员

    权限运行此API,那么默认的安全描述符将有具有管理员权限。


    • 已编辑 Skyseer 2012年12月11日 5:26
    2012年12月11日 5:25
  • 哦啊,误会了,对不起啊。进程间通讯应该用什么呢?消息?

    1.NamedPipe服务肯定启动了。就像我最后说的,我的服务端和客户端都启动的状态,如果把问题机器上的其中一个奇怪的服务打开,通讯就会被中断。如果把这个奇怪的服务关掉,那我的通讯又可以继续进行了。

    2.确实可以,如果没有那个奇怪的服务。

    3.用的是None。

    4.OS版本我在Win7,8上跑没问题,客户用的是8。而且那个奇怪的服务应该是win8的版本,所以先把目标平台假设就在8上吧。我估计7也会有同样的问题。

    5,如果是UAC的话应该好办,因为本身我的应用跑的就是管理员权限。

    因为做了WCF Trace,看不出什么所以然来。所以我的想法是,首先如果能找到比较官方或者权威的使用命名管道运作的应用,

    那么可以先排除不是我的应用出问题,而是这个服务随便就把使用管道通讯的应用给干掉了。

    然后我再继续找有什么办法绕开该服务。

    这个服务应该是某公司做出来,说是可以远程控制注册到它上面的家电的电源的。不知道启动它之后会对OS作什么事情。

    按照常理来说,一个应用服务的启动终止不应该会对OS的通讯做那么大的改动的吧。



    野老

    2012年12月11日 5:39
  • 我不清楚你说的“服务”是什么东西,我可能会把它理解为 Windows 任务管理器里的服务。

    我假设建立一个使用NamedPipe的服务程序Server.exe,然后建立一个使用此服务的客户端程序 Client.exe,

    将 Server.exe和Client.exe部署到机器A上,如果能正常运行就没有问题。

    这个时候,你再引入你的“服务”。

    2012年12月11日 6:02
  • 我说的”奇怪的服务”就是任务管理器里面的服务,通过计算机管理的”服务和应用”->”服务”也可以看到,测试的时候,我是在管理里面在里面把它开启和关闭。
    原先我贴的代码还是太长,我把它全部精简到只有下面这十来行之后,就像你说的,把Server.exe和Client.exe部署到机器A上,正常运行,

    但是启动这个”奇怪的服务”之后,在执行

    pipeProxy.ReverseString(str)

    就会出现最开始时候的异常了。

    首先,在这里你可以看到,我的服务端和客户端都是Transport,你可以改成None,这个没关系。

    所以我想知道,到底是在哪里被这个”奇怪的服务”干掉了。

    using (var host = new ServiceHost(typeof(StringReverser), new[] { new Uri("net.pipe://localhost") }))
     {
         host.AddServiceEndpoint(typeof(IStringReverser), new NetNamedPipeBinding(), "PipeReverse");
         host.Open();
    
         Console.WriteLine("Service is available. Press <ENTER> to exit.");
         Console.ReadLine();
    
         host.Close();
      }


    var pipeFactory = new ChannelFactory<IStringReverser>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/PipeReverse"));
    var pipeProxy = pipeFactory.CreateChannel();
    
    while (true)
    {
       var str = Console.ReadLine();
       Console.WriteLine("pipe: " + pipeProxy.ReverseString(str));
    }



    野老



    2012年12月11日 6:11
  • 如果那个“奇怪的服务”在运行的时候,你的服务端能够启动成功(也就是host.Open() 成功)吗?
    2012年12月11日 6:15
  • 可以,只有在我上面贴的那句,客户端调用Endpoint的C的时候,才会发生问题。

    野老

    2012年12月11日 6:18
  • 如果你不启动 StringReverser 的话,pipeProxy.ReverseString(str)) 会报什么错误(记得那个“奇怪的服务”一定要打开)?
    2012年12月11日 6:30
  • 确实,“奇怪的服务”一定要打开,自己的服务不开,Client去链接的时候,会报同样的异常。

    但是如果“奇怪的服务”不开的话,那么出来的异常就是指定的地址不存在了。

    所以我的感觉是,肯定是那个“奇怪的服务”做了什么一把抓的处理。

    是怎么才能达到无条件拦截所有pipe的信息的呢?


    野老


    2012年12月11日 7:05
  • 你可以修改下你的服务地址试试。

    不过确实可以编写拦截IPC的程序。

    可以的话,可以和对方的开发人员沟通下,了解下他们是如何实现的,再想对策。

    2012年12月11日 7:32
  • 服务地址修改过了的,没什么作用。

    现在是想办法跟对方开发人员沟通了。

    请教一下,有没有什么现成的拦截IPC的程序?



    野老

    2012年12月11日 7:42
  • 这个,360?
    2012年12月11日 7:52
  • 果然跟我想的一样……360这货在我们公司的机器上被报木马。

    之前我吃过一次亏。

    这个可以看拦截命名管道的信息?怎么做?如果可以的话,我去申请尝试一把。


    野老

    2012年12月11日 7:56
  • 我也只是猜测,其它的我也不知道。一般防火墙软件都会关照IPC访问。但是防火墙不会告诉你是IPC,用户看不懂,而只是告诉你某个进程有个危险操作。
    • 已编辑 Skyseer 2012年12月11日 8:07
    2012年12月11日 8:07
  • 启动的时候,自带防火墙会说,你要不要让这个东西访问网络。我觉得很奇怪,是不是用管理员用户的时候,不用选也会自动加入允许列表?

    但是就算关照,这也是我访问方的事情啊,接收方无缘无故拦截掉,这个如果不是杀软做的事情,会不会留下痕迹?


    野老

    2012年12月11日 8:15
  • 又发现一个问题。

    这个奇怪的服务指定平台是win8,我把它弄到7上面,这个服务是可以运行的。

    姑且认为这个时候它的所有功能都开启了。

    7+ fm4.5 + 奇怪的服务 的情况下,居然是没有发生异常。

    也就是说,发生异常的环境是

    8+fm4.5 + 奇怪的服务 。


    野老

    2012年12月11日 12:04
  • 好不容易跟对方大牌开发人员联系上了。

    说是他们用的是 net.pipe://echonet 的Pipe指定地址,

    然后终端用DuplexChannelFactory双工通道通信。

    这有什么关系吗?


    野老

    2012年12月14日 1:35
  • 我也做了一个Service,一样注册到windowsService中去。

    发现和那个奇怪的服务冲突了。应该是baseAddress的问题。

    我如何指定特定的Address到我的服务?

    果然,我开始以为奇怪的服务用的是 net.pipe://echonet+他们的标识符+C。

    但是实际上试下来,应该是没有 他们的标识符,而是直接net.pipe://echonet+C。

    这样的话,在同一台极其上面跑的pipe,都会被这个启动的服务给抓掉。

    这尼玛郁闷大了。他们想干什么。eventlog里面,他们的服务名字叫Service1。我靠……


    野老


    2012年12月14日 4:56
  • 调查到这里基本上结束了。

    原因出在这个 奇怪的服务 的地址指定上。

    原因1,奇怪的服务 运行在WindowsService上,以管理者权限运行的,并且访问地址指定的是net.pipe根地址,以至于所有的pipe访问都被它抓了。

    原因2,我的服务是运行在应用程序上的,仅仅是作为普通用户运行,终端访问的时候,抢不过 奇怪的服务。

    对策1,起个服务把这个外包培训学校做的东西给关了。

    对策2,把我的服务直接上升到顶层命名空间,让我的服务访问时,能够直接访问到我指定的地址。

    疑问1,为什么在win7上没有这个问题,win8上有这个问题。难道WCF在4.5上的访问机制变了?(NetPeerTCPBinding已经标记为过时,虽然没有去找变更文档,但是可以知道4.5里面肯定做了某些变化。)


    野老

    2012年12月14日 7:57
  • 你们真厉害,都用的WCF,还互相拆台。
    2012年12月14日 9:00