积极答复者
ProtocolException的问题

问题
-
做了一个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。
请问,有朋友知道,这具体是因为系统环境做了什么特定的设置而导致的结果吗?
野老
答案
-
调查到这里基本上结束了。
原因出在这个 奇怪的服务 的地址指定上。
原因1,奇怪的服务 运行在WindowsService上,以管理者权限运行的,并且访问地址指定的是net.pipe根地址,以至于所有的pipe访问都被它抓了。
原因2,我的服务是运行在应用程序上的,仅仅是作为普通用户运行,终端访问的时候,抢不过 奇怪的服务。
对策1,起个服务把这个外包培训学校做的东西给关了。
对策2,把我的服务直接上升到顶层命名空间,让我的服务访问时,能够直接访问到我指定的地址。
疑问1,为什么在win7上没有这个问题,win8上有这个问题。难道WCF在4.5上的访问机制变了?(NetPeerTCPBinding已经标记为过时,虽然没有去找变更文档,但是可以知道4.5里面肯定做了某些变化。)
野老
- 已标记为答案 geminiyellow 2012年12月14日 7:57
全部回复
-
把你客户端和服务端的 配置文件贴出来 看看。问题应该就在配置上
Frank Xu Lei--谦卑若愚,好学若饥
[老徐的网站]:http://www.frankxulei.com/[老徐的博客]:http://54peixun.com/Author/frankxulei
微软WCF中文技术论坛
微软WCF英文技术论坛
Windows Azure中文技术论坛 -
谢谢老徐。简要来说,关于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(); } } }
野老
- 已编辑 geminiyellow 2012年12月4日 1:43
-
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中文技术论坛 -
上面那个代码里面有个注释
//★开始怀疑是这里,没指定SecurityMode的时候是传输加密
出问题的时候我查过了,用NONE放上去之后,还是出问题。所以完全没有头绪到底是怎样了。
出log比较了在客户处以及在公司环境下的记录之后,发现在客户那边出问题的地方,
匿名广播ServiceHost.open之后,正常log会有一句特定的EndPointAddress被指定,但是异常log里面没有这句。
再往下就是那个双方安全等级不一致的情况了。
我还怀疑是用户那边直接就把网络通讯给掐断了。
但是想想人家是店头,要用网络的。所以想不通是什么原因了。
野老
- 已编辑 geminiyellow 2012年12月6日 9:25
-
挨个关服务,发现关掉其中某一个用户用来做远程控制的服务之后,执行就成功了。
channel.Register(_clientInstance.Info);
这个部分,如果这个服务启动起来的话,是无法成功的。
Update: 最后确认了,果然是把NetNamedPipe给屏蔽掉了。
只起来一个ServiceHost,
A:net.pipe://localhost/PipeReverse
B:NetNamedPipeBinding
C:VS2010默认的WCFApplication给的IService
Client侧,CreateChannel后,去使用C的时候,立刻
ProtocolException
野老
- 已编辑 geminiyellow 2012年12月7日 7:12
-
不是说这个不能用,我只是回答“老徐有什么现行的用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
-
哦啊,误会了,对不起啊。进程间通讯应该用什么呢?消息?
1.NamedPipe服务肯定启动了。就像我最后说的,我的服务端和客户端都启动的状态,如果把问题机器上的其中一个奇怪的服务打开,通讯就会被中断。如果把这个奇怪的服务关掉,那我的通讯又可以继续进行了。
2.确实可以,如果没有那个奇怪的服务。
3.用的是None。
4.OS版本我在Win7,8上跑没问题,客户用的是8。而且那个奇怪的服务应该是win8的版本,所以先把目标平台假设就在8上吧。我估计7也会有同样的问题。
5,如果是UAC的话应该好办,因为本身我的应用跑的就是管理员权限。
因为做了WCF Trace,看不出什么所以然来。所以我的想法是,首先如果能找到比较官方或者权威的使用命名管道运作的应用,
那么可以先排除不是我的应用出问题,而是这个服务随便就把使用管道通讯的应用给干掉了。
然后我再继续找有什么办法绕开该服务。
这个服务应该是某公司做出来,说是可以远程控制注册到它上面的家电的电源的。不知道启动它之后会对OS作什么事情。
按照常理来说,一个应用服务的启动终止不应该会对OS的通讯做那么大的改动的吧。
野老
-
我说的”奇怪的服务”就是任务管理器里面的服务,通过计算机管理的”服务和应用”->”服务”也可以看到,测试的时候,我是在管理里面在里面把它开启和关闭。
原先我贴的代码还是太长,我把它全部精简到只有下面这十来行之后,就像你说的,把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)); }
野老
- 已编辑 geminiyellow 2012年12月11日 6:13
-
确实,“奇怪的服务”一定要打开,自己的服务不开,Client去链接的时候,会报同样的异常。
但是如果“奇怪的服务”不开的话,那么出来的异常就是指定的地址不存在了。
所以我的感觉是,肯定是那个“奇怪的服务”做了什么一把抓的处理。
是怎么才能达到无条件拦截所有pipe的信息的呢?
野老
- 已编辑 geminiyellow 2012年12月11日 7:06
-
我也做了一个Service,一样注册到windowsService中去。
发现和那个奇怪的服务冲突了。应该是baseAddress的问题。
我如何指定特定的Address到我的服务?
果然,我开始以为奇怪的服务用的是 net.pipe://echonet+他们的标识符+C。
但是实际上试下来,应该是没有 他们的标识符,而是直接net.pipe://echonet+C。
这样的话,在同一台极其上面跑的pipe,都会被这个启动的服务给抓掉。
这尼玛郁闷大了。他们想干什么。eventlog里面,他们的服务名字叫Service1。我靠……
野老
- 已编辑 geminiyellow 2012年12月14日 5:58
-
调查到这里基本上结束了。
原因出在这个 奇怪的服务 的地址指定上。
原因1,奇怪的服务 运行在WindowsService上,以管理者权限运行的,并且访问地址指定的是net.pipe根地址,以至于所有的pipe访问都被它抓了。
原因2,我的服务是运行在应用程序上的,仅仅是作为普通用户运行,终端访问的时候,抢不过 奇怪的服务。
对策1,起个服务把这个外包培训学校做的东西给关了。
对策2,把我的服务直接上升到顶层命名空间,让我的服务访问时,能够直接访问到我指定的地址。
疑问1,为什么在win7上没有这个问题,win8上有这个问题。难道WCF在4.5上的访问机制变了?(NetPeerTCPBinding已经标记为过时,虽然没有去找变更文档,但是可以知道4.5里面肯定做了某些变化。)
野老
- 已标记为答案 geminiyellow 2012年12月14日 7:57