【发布时间】:2014-01-12 00:32:56
【问题描述】:
在您将问题读到最后之前,请不要关闭重复;我已经用谷歌搜索了几个小时没有成功。
编辑:现在我确信它与 WCF 缓存打开的 TCP 连接(连接池)的方式有关。请查看问题末尾的编辑#5。
我基本上有一个使用netTcpBinding 配置的WCF 服务。即使我优雅地关闭客户端代理(参见下面的代码),服务器始终会记录“System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host”。
我已将问题范围缩小到我可以编写的最基本的 WCF 示例。我在 WCF 跟踪生成的日志中遇到异常每次我关闭客户端应用程序。不过,我自己的代码中没有出现任何异常,这意味着它按预期工作,我无法调试任何东西来查看 WCF 在我的日志中添加错误时出了什么问题。
服务接口/实现:
[ServiceContract]
public interface IService1
{
[OperationContract]
string DoWork();
}
...
public class Service1 : IService1
{
public string DoWork()
{
return "12";
}
}
服务器端配置:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="WebApplication1.Service1">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="netTcpEndpointBinding" contract="WebApplication1.IService1" />
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="netTcpEndpointBinding">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
客户端配置:
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_IService1">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost/WebApplication1/Service1.svc"
binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IService1"
contract="ServiceReference1.IService1" name="NetTcpBinding_IService1" />
</client>
</system.serviceModel>
</configuration>
使用服务的客户端代码(VS2012 使用“添加服务引用”为我生成客户端代理):
private async Task<string> 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
但是一旦应用程序终止,服务器就会记录一个异常。我知道我不应该担心这个异常,因为它按预期工作(异常只出现在日志中)并且无论如何都可能发生,以防客户端在调用 .Close()/.Abort() 之前突然终止。
但是,这是正常行为吗?我的意思是,如果我正确关闭了我的客户端代理,我希望服务器不记录异常(这会污染我的日志)。 我还假设在关闭客户端代理后,客户端和服务器之间仍然建立了一些 TCP 连接(未知状态),因为服务器仅在整个客户端应用程序终止后才记录异常。如果这样的连接仍然打开,这不会引入意外行为(例如最大连接客户端数)吗?这真的是预期的吗?
我发现了关于这个问题的不同线索:
wcf "An existing connection was forcibly closed by the remote host" after closing client
结论是“不在乎”。
有人可以通过一些参考资料确认并解释为什么会抛出此异常吗?
编辑:
异常的日志跟踪:
<Exception>
<ExceptionType>System.Net.Sockets.SocketException, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>An existing connection was forcibly closed by the remote host</Message>
<StackTrace>
à 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)
</StackTrace>
<ExceptionString>System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host</ExceptionString>
<NativeErrorCode>2746</NativeErrorCode>
</Exception>
非常感谢
编辑 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 使用基于服务的 TCP 连接池 主机 DNS 名称和服务正在侦听的端口号。这 当客户端调用不同的服务时效果很好 不同的端口或服务托管在一个进程中并共享 一个港口。如果一个客户端调用多个服务共享一个端口 托管在不同的进程中,或者托管在 WAS/IIS 中,客户端 边池可能会导致与服务 A 的连接出现问题 为服务 B 重用,导致抛出异常, 连接中止,并创建了一个新通道。为了避免这个问题, 使用 CustomBinding 并指定不同的 ConnectionPoolSettings.GroupName 为客户端的每个服务 通信。
所以现在我的问题是:如果这是正常行为,我该怎么做才能防止我的日志因所有这些异常而受到污染?
【问题讨论】:
-
可能与控制台应用程序中的 async/await 有关吗?我记得在某处读到 async/await 由于线程问题不适合控制台应用程序。尝试在非异步方法中执行此操作,看看是否会出现相同的问题。
-
@Tim 我现在无法测试,但是是的,控制台应用程序和 GUI 应用程序之间存在区别:线程模型,即线程池与主 UI 线程。我不认为问题来自这里,因为它是我使用 WPF 构建的真实应用程序的简化示例。
-
还有一些想法。一,如果您还没有启用 WCF 跟踪。其次,我想知道
finally块是否可能是问题的根源。我只是把它放在那里,因为从合理的角度看,我没有看到您的代码有问题,但是如果在try块中发生了某些事情,您可能会尝试在频道上调用Close()处于不良或故障状态。当然,您在catch块中将其设置为null,但是如果发生了一些有趣的事情并且不是捕获的异常怎么办? -
@Tim 现在我今天再次阅读了我的问题,我想可能不是那么清楚。我实际上只在日志中看到这些异常(在激活 WCF 跟踪之后)。这意味着调试器根本不会抱怨异常。对于
finally块,是的,如果代理在await之后和Close之前由于任何原因进入故障状态,它肯定会抛出异常。 -
嗯....服务是否又调用了另一个服务?根据我自己的经验,我知道读取跟踪日志可能具有挑战性——日志中是否有任何迹象表明异常在通信流中的哪个位置被引发?是否有可能其他连接没有正确关闭并且这些连接出现故障或超时?
标签: c# .net wcf tcp nettcpbinding