如何正确关闭客户端代理(远程主机强行关闭现有连接)?
在你将问题读到最后之前,请不要关闭。 我已经谷歌搜索了几个小时没有成功。
编辑:现在我确信它与WCF缓存打开TCP连接(连接池)的方式有关。 请在问题结尾处查看编辑#5。
我基本上有一个使用netTcpBinding
配置的WCF服务。 即使我正常关闭客户端代理(请参阅下面的代码),服务器始终记录“ System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host
”。
我已经将问题缩小到我能写的最基本的WCF示例。 我每次关闭客户端应用程序时都会在WCF跟踪生成的日志中获得exception。 我在自己的代码中没有得到任何exception,这意味着它按预期工作,我无法调试任何东西,看看WCF在我的日志中添加错误出了什么问题。
服务接口/实现:
[ServiceContract] public interface IService1 { [OperationContract] string DoWork(); } ... public class Service1 : IService1 { public string DoWork() { return "12"; } }
服务器端配置:
客户端配置:
使用该服务的客户端代码(VS2012使用“添加服务引用”为我生成了客户端代理):
private async Task TestTask() { Service1Client proxy = null; try { Console.WriteLine("Calling service"); proxy = new Service1Client(); return await proxy.DoWorkAsync(); } finally { if (proxy.State != System.ServiceModel.CommunicationState.Faulted) { Console.WriteLine("Closing client"); proxy.Close(); } else { Console.WriteLine("Aborting client"); proxy.Abort(); } } }
一切正常:
致电服务
关闭客户
12
但是一旦应用程序终止,服务器就会记录一个exception。 我理解我不应该担心这个exception,因为它按预期工作(exception只出现在日志中),如果客户端在调用.Close()
/ .Abort()
之前突然终止,可能会发生这种情况。
但是,这仍然是正常行为吗? 我的意思是,如果我正确关闭了我的客户端代理,我希望服务器不会记录exception(即污染我的日志)。 我还假设在关闭客户端代理之后,客户端和服务器之间仍然建立了一些TCP连接(未知状态),因为服务器仅在整个客户端应用程序终止后才记录exception。 如果这样的连接仍然打开,这不会引入意外行为(例如连接客户端的最大数量)? 这真的是预期的吗?
我找到了关于这个问题的不同主题:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/0f548f9b-7051-46eb-a515-9185f504d605/error-using-nettcpbinding-an-existing-connection-was-forcibly-closed-by-在远程主机?论坛= WCF
wcf“关闭客户端后,远程主机强行关闭现有连接”
结论将是“不关心它”。
有人可以通过一些参考确认,并解释为什么抛出这个exception?
编辑:
记录exception的日志:
System.Net.Sockets.SocketException, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 An existing connection was forcibly closed by the remote host à System.ServiceModel.Channels.SocketConnection.HandleReceiveAsyncCompleted() à System.ServiceModel.Channels.SocketConnection.OnReceiveAsync(Object sender, SocketAsyncEventArgs eventArgs) à System.Net.Sockets.SocketAsyncEventArgs.FinishOperationAsyncFailure(SocketError socketError, Int32 bytesTransferred, SocketFlags flags) à System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped) à System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP) System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host 2746
非常感谢
编辑2:我在IIS中托管服务或在Windows服务中自托管时遇到同样的问题
编辑3: 这是一个重现问题的完整示例 : http : //speedy.sh/ENB59/wcf-test.zip
编辑4:
我试图用WCF为我建立的TCP连接来监控实际发生的事情。
关闭客户端代理后,我仍然看到与我的服务器打开的TCP连接:
我假设这与将被缓存以供将来重用(即连接池)的TCP连接有关,因为打开与服务器的新连接(在第一个客户端代理关闭之后)不会创建新的TCP连接。 如果我调用Console.WriteLine(new Test().TestTask().Result);
在我的应用程序中两次,我仍然只看到一个打开的TCP连接。
我还注意到,如果我在关闭客户端通道后等待太久,这个连接因为超时而死亡。
编辑5:好的,我在MSDN上找到了关于该连接池的文档:
NetTcpBinding根据服务的主机DNS名称和服务正在侦听的端口号使用TCP连接池。 当客户端在不同端口上调用不同服务,或者服务在单个进程中托管并共享端口时,这种方法很有效。 如果单个客户端调用多个服务共享托管在不同进程中的端口,或托管WAS / IIS,则客户端池可能会导致与服务A的连接重新用于服务B的问题,从而导致抛出exception,连接中止,并创建了一个新频道。 要避免此问题,请使用CustomBinding并为客户端与之通信的每个服务指定不同的ConnectionPoolSettings.GroupName。
所以现在我的问题是:如果这是一个正常的行为,我该怎么做才能防止我的日志被所有这些例外污染?
要解决此错误,只需关闭Channel Factory即可。
private async Task TestTask() { Service1Client proxy = null; try { Console.WriteLine("Calling service"); proxy = new Service1Client(); return await proxy.DoWorkAsync(); } finally { if (proxy.State != System.ServiceModel.CommunicationState.Faulted) { Console.WriteLine("Closing client"); proxy.ChannelFactory.Close(); proxy.Close(); } else { Console.WriteLine("Aborting client"); proxy.Abort(); } } }
编辑确定。 我能够使用您的代码重现问题。 好消息是我偶然发现了一种不能重现它的方法。 我认为您的代码中没有任何问题。 我认为正在记录错误,因为您在VS中的调试器中运行它; 当调试器关闭服务时导致错误被记录。
按照以下步骤操作,看看你是否不再收到错误(这对我来说每次都很合适):
- 右键单击服务项目,然后选择“调试”>“启动新实例”
- 右键单击控制台应用程序,然后选择“调试”>“启动新实例”
- 运行控制台以完成。
- 检查您的日志文件。
- 使用WCF测试客户端窗口停止服务,而不是调试器停止按钮。
- 检查您的日志文件。
这是我执行上述步骤的冲击波video的链接: swf文件
原始您发布的代码在我的系统上运行得很好。 我在任何日志中都没有收到任何错误。 顺便说一句,我会完全摆脱catch块,因为它只是重新抛出exception。 然后我会像下面这样写下finally块。 我认为它使代码更清晰,并向读者传达了如果抛出exception就没有做任何事情的想法。
Service1Client proxy = null; try { Console.WriteLine("Calling service"); proxy = new Service1Client(); return await proxy.DoWorkAsync(); } finally { if (proxy != null) { if (proxy.State == CommunicationState.Faulted) { Console.WriteLine("Aborting client"); proxy.Abort(); } else { Console.WriteLine("Closing client"); proxy.Close(); } } }
我怀疑你在try块中的return命令导致执行跳过finally块,导致你的连接保持打开,直到客户端关闭导致exception。 这可能吗? 你确定你的finally块被执行了吗?