我正在编写一个只接收来自本地主机的电话的服务.性能很重要,所以我想我会尝试NetNamedPipeBinding而不是NetTcpBinding,看看我是否能看到任何明显的性能提升.
如果客户端在对服务器执行了一个或多个请求之后,在较长时间内处于空闲状态,则下一个请求似乎会因绑定中的某些空闲超时而失败.服务重新启动时也会发生同样的事情.
我需要我的客户端能够在允许的情况下保持连接打开,以避免与设置新连接相关的开销.我还需要能够不时重新启动服务,并让客户端在发现连接已终止时自动重试.
我知道这可以通过NetTcpBinding中的可靠性内容来支持,但是如何在NetNamedPipeBinding中获得相同级别的重新连接可靠性呢?它甚至可能吗?
这个问题在某种程度上是学术性的,因为它不是使用NetNamedPipes的要求,我可以很容易地采用它来使用tcp绑定,但它是一个痒,我真的想要抓它.
我的经验是,当使用NetNamedPipes时,绑定上的"ReceiveTimout"功能就像"不活动超时"而不是接收时间.请注意,这与NetTCPBinding的工作方式不同.使用TCP,它确实是一个接收超时,你可以通过可靠的消息传递配置一个单独的非活动超时.(它似乎也与SDK文档相反,但很好).
要解决此问题,请在创建绑定时将RecieveTimout设置为较大的值.
例如,如果您在程序上创建绑定...
NetNamedPipeBinding myBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None); myBinding.ReceiveTimeout = TimeSpan.MaxValue;
或者,如果您关心以声明方式创建绑定...
我没有在WCF中使用过NetNamedPipes,但是我花了更多的时间来学习NetTcp的超时值.我使用以下配置为我的NetTcpBindings,并祝好连接保持活跃.
服务器:
客户:
我花费最多时间的重要设置是sendTimeout和receiveTimeout.如果您的receiveTimeout与您的发送相同或更少,则一旦达到超时,通道将会丢弃.如果接收较高且发送高于阈值,则通道将触发传输级别keepalive.从我的测试中,sendTimeout阈值是30秒.任何低于此值的东西都不会被发送.
此外,我有一个基于计时器的keepalive调用,我每分钟执行一次尝试,以确保通道正常运行.该调用只是一个布尔返回成员:
[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false)] bool KeepAlive(); public bool KeepAlive() { return true; }
您还可以获取频道事件(如果您在正确的时间获得它们)并在发生错误时重新打开连接:
InstanceContext site = new InstanceContext(this); _proxy = new MyServiceChannel(site); if (_proxy != null) { if (_proxy.Login()) { //Login was successful //Add channel event handlers so we can determine if something goes wrong foreach (IChannel a in site.OutgoingChannels) { a.Opened += Channel_Opened; a.Faulted += Channel_Faulted; a.Closing += Channel_Closing; a.Closed += Channel_Closed; } } }
我希望其中一些可以通过NetNamedPipes进行翻译并对您有价值.
编辑:捕获服务器重新启动问题的更多选项
当服务器重新启动时,它应该导致客户端的通道关闭或出错.在客户端捕获这些事件将使您可以选择使用重新连接计时器,直到服务再次可用.
private void Channel_Faulted(object sender, EventArgs e) { IChannel channel = sender as IChannel; if (channel != null) { channel.Abort(); channel.Close(); } //Disable the keep alive timer now that the channel is faulted _keepAliveTimer.Stop(); //The proxy channel should no longer be used AbortProxy(); //Enable the try again timer and attempt to reconnect _reconnectTimer.Start(); } private void _reconnectTimer_Tick(object sender, System.EventArgs e) { if (_proxy == null) { InstanceContext site = new InstanceContext(this); _proxy = new StateManagerClient(site); } if (_proxy != null) { if (_proxy.Login()) { //The connection is back up _reconnectTimer.Stop(); _keepAliveTimer.Start(); } else { //The channel has likely faulted and the proxy should be destroyed AbortProxy(); } } } public void AbortProxy() { if (_proxy != null) { _proxy.Abort(); _proxy.Close(); _proxy = null; } }
您可能希望确保重新连接计时器的登录尝试是在后台线程上异步完成的,这样每次尝试登录时它们都不会挂起UI.因人而异
我一直在研究连接两天的TCP连接丢失的问题,并得出结论,很多人在WCF中建立连接时错过了一个重要的观点.每个人似乎都在做的是创建一个频道,然后尝试在应用程序的生命周期中保持它,播放各种肮脏的技巧以保持TCP会话存活.这不是它的意思.
您应该创建一个频道,在您的服务上执行一个(或在第一个之后不久)调用,然后关闭并处置该频道.这将给你(几乎)无状态操作,你不必为保持会话活着而烦恼,这是你不应该首先想要的.
您会发现创建和关闭通道(来自重用的ChannelFactory)的开销可以忽略不计,在典型的机器上只需要几十纳秒.
每个人都在启动的receiveTimeout属性定义了一个频道在被自动删除之前可以保持空闲的时间,这告诉你频道不应该保持打开很长时间(默认为1分钟).如果将receiveTimeout设置为TimeSpan.MaxValue,它将使您的频道保持更长时间,但这不是它的用途,也不是您在实际场景中想要的.
最终让我走上正轨的是 http://msdn.microsoft.com/en-us/library/ms734681.aspx ,它提供了一个可怕的错误示例,但确实展示了如何使用ChannelFactory.响应者指出错误并设置记录,所以总而言之,你可以在这里得到你需要的一切.
然后,我所有的问题都结束了.不再"尝试对不是套接字的东西进行操作"而不再"远程主机强行关闭现有连接".