如何在 WCF 中自动重新连接命名管道绑定
Posted
技术标签:
【中文标题】如何在 WCF 中自动重新连接命名管道绑定【英文标题】:How can I make Named Pipe binding reconnect automatically in WCF 【发布时间】:2010-09-25 07:51:05 【问题描述】:我正在编写一个只接收来自本地主机的调用的服务。性能很重要,所以我想我会尝试使用NetNamedPipeBinding 而不是NetTcpBinding,看看是否能看到任何明显的性能提升。
如果客户端在向服务器执行了一个或多个请求后,长时间处于空闲状态,则下一个请求似乎由于绑定中的一些空闲超时而失败。当服务重新启动时也会发生同样的事情。
我需要我的客户能够在允许的时间内保持连接打开,以避免与设置新连接相关的开销。我还需要能够不时重新启动服务,并让客户端在注意到连接已终止时自动重试。
我知道 NetTcpBinding 中的可靠性内容支持这一点,但是如何在 NetNamedPipeBinding 中获得相同级别的重新连接可靠性?有可能吗?
这个问题有点学术性,因为它不是使用 NetNamedPipes 的要求,我可以很容易地采用它来使用 tcp 绑定,但它很痒,我真的很想从头开始。
【问题讨论】:
IIRC,CreateNamedPipe 的倒数第二个参数(NamedPipeBinding 下的非托管 win32 函数 - msdn.microsoft.com/en-us/library/windows/desktop/…)充当客户端连接超时,相当短。这可能与您在服务器启动时看到的超时有关;也许您可以使用反射器/dotpeek/调试器来查看哪些参数从 WCF 传递到本机函数,以及是否可以使用配置更改这些参数 既然问题是同一个学术问题,一般我会这样处理:看看调用了哪些本机函数,有哪些超时,然后追溯这些函数在托管代码中的调用位置,查看参数的来源。很长,但很有趣,可以帮助你弄清楚它是如何工作的 :) 我用这种方式调试了一个 Sharepoint 问题...... 【参考方案1】:我的经验是,当使用 NetNamedPipes 时,绑定功能上的“ReceiveTimout”类似于“Inactivity Timeout”而不是接收 timout。请注意,这与 NetTCPBinding 的工作方式不同。使用 TCP,它确实是一个接收超时,并且有一个单独的不活动超时,您可以通过可靠的消息传递进行配置。 (这似乎也与 SDK 文档相反,但是哦,好吧)。
要解决此问题,请在创建绑定时将 RecieveTimout 设置为较大的值。 例如,如果您在程序上创建绑定...
NetNamedPipeBinding myBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
myBinding.ReceiveTimeout = TimeSpan.MaxValue;
或者,如果您关心以声明方式创建绑定...
<netNamedPipeBinding>
<binding name="myBinding" receiveTimeout="infinite">
</binding>
</netNamedPipeBinding>
【讨论】:
感谢您的提示。我已将我的(接收)超时设置为 20:00(20 分钟),并且我的所有操作都没有花费超过 2 分钟......甚至可能更可能 5 秒,并且事情正在发生。人们还应该使用同样吸引我的 using 语句来查看这个微软的“功能”。 ***.com/questions/573872/…【参考方案2】:我没有在 WCF 中使用过 NetNamedPipes,但我花在学习 NetTcp 超时值上的时间比我关心的要多。我为我的 NetTcpBindings 使用了以下配置,并祝连接保持活跃。
服务器:
<binding name="MyBindingName" sendTimeout="00:00:30" receiveTimeout="infinite">
<reliableSession enabled="true" inactivityTimeout="00:05:00" ordered="true" />
<security mode="None" />
</binding>
客户:
<binding name="MyBindingName" closeTimeout="00:00:30" openTimeout="00:00:30" receiveTimeout="infinite" sendTimeout="00:00:30">
<reliableSession enabled="true" inactivityTimeout="00:01:00" ordered="true" />
<security mode="None" />
</binding>
我花费最多时间的重要设置是 sendTimeout 和 receiveTimeout。如果您的 receiveTimeout 与您的 send 相同或小于您的 send,则一旦达到该超时,通道将丢弃。如果接收更高并且发送高于阈值,则通道将触发传输级别保持活动。根据我的测试,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。 YMMV
【讨论】:
感谢 Chris,但不幸的是,修改超时和添加 keepalive 方法只能解决我的两个问题中的第一个。重新启动服务后,我仍然会遇到同样的问题。 如何以编程方式设置绑定/reliableSession?【参考方案3】:我这两天一直在研究 TCP 连接断开的问题,得出的结论是很多人在 WCF 中设置连接时遗漏了一个关键点。每个人似乎都在做的是创建一个通道,然后尝试在应用程序的整个生命周期中保持它,玩各种肮脏的技巧来保持 TCP 会话的活动。这不是它的本意。
您应该创建一个通道,对您的服务执行一次(或在第一次之后不久)调用,然后关闭并处置该通道。这将为您提供(实际上)无状态操作,并且您不必为保持会话活动而烦恼,这是您一开始就不应该想要的。
您会发现创建和关闭通道(来自重用的 ChannelFactory)的开销可以忽略不计,在典型机器上只需几十纳秒。
每个人都在启动的 receiveTimeout 属性定义了通道在自动删除之前可以保持空闲的时间,这告诉您通道不应该保持打开很长时间(默认值为 1 分钟)。如果您将 receiveTimeout 设置为 TimeSpan.MaxValue,它将使您的频道保持更长的时间,但这不是它的用途,也不是您在实际场景中想要的。
最终让我走上正轨的是 http://msdn.microsoft.com/en-us/library/ms734681.aspx 它提供了一个可怕的错误示例,但确实显示了应该如何使用 ChannelFactory。响应者指出错误并直接记录,因此总而言之,您可以在这里获得所需的一切。
然后,我所有的问题都解决了。不再有“尝试对不是套接字的东西进行操作”,也不再有“现有连接被远程主机强制关闭”。
【讨论】:
social.msdn.microsoft.com/forums/en-US/wcf/thread/… - Dave Cliffe (MSFT) 建议这是一项相当昂贵的操作,如果可能的话应该重用频道 @Martin:“创建和关闭频道的开销……可以忽略不计”。这不取决于安全协商之类的东西吗?我想与 AD 服务器之类的东西重新协商凭据绝非微不足道。 @romkyns:这里有一篇很好的 MSDN 博客,介绍了 .NET 3.5 对 ClientBase 的性能改进:blogs.msdn.com/wenlong/archive/2007/10/27/… “双工”通信怎么样?服务器到客户端的事件等? 是的,我什至想知道双工通道的情况。如果客户端在订阅后关闭频道,服务器如何将数据发送回订阅的客户端?我正在尝试使用客户端订阅服务器事件的那个频道。以上是关于如何在 WCF 中自动重新连接命名管道绑定的主要内容,如果未能解决你的问题,请参考以下文章