问题描述
在你把问题读到最后之前,请不要重复关闭;我已经用谷歌搜索了几个小时都没有成功.
现在我确信这与 WCF 缓存打开 TCP 连接(连接池)的方式有关.请看一下他问题末尾的编辑 #5.
我基本上有一个使用 netTcpBinding
配置的 WCF 服务.即使我优雅地关闭客户端代理(见下面的代码),服务器总是记录System.Net.Sockets.SocketException (0x80004005): 一个现有的连接被远程主机强行关闭
".
我已将问题缩小到我可以编写的最基本的 WCF 示例.我在 WCF 跟踪每次关闭客户端应用程序时生成的日志中都会遇到异常.不过,我在自己的代码中没有收到任何异常,这意味着它按预期工作,我无法调试任何内容以查看 WCF 在我的日志中添加错误时出了什么问题.
服务接口/实现:
[ServiceContract]公共接口 IService1{【经营合同】字符串 DoWork();}...公共类 Service1 : IService1{公共字符串 DoWork(){返回12";}}
服务端配置:
<system.web><编译调试=真"目标框架=4.5";/><httpRuntime targetFramework="4.5";/></system.web><system.serviceModel><行为><服务行为><行为名称="><serviceMetadata httpGetEnabled="true";httpsGetEnabled=真"/><serviceDebug includeExceptionDetailInFaults="true";/></行为></serviceBehaviors></行为><serviceHostingEnvironment aspNetCompatibilityEnabled="true";multipleSiteBindingsEnabled=真";/><服务><服务名称=WebApplication1.Service1"><端点地址="绑定=netTcpBinding"bindingConfiguration=netTcpEndpointBinding";合同=WebApplication1.IService1";/></服务></服务><绑定><netTcpBinding><绑定名称=netTcpEndpointBinding"><安全模式=无"/></binding></netTcpBinding></绑定></system.serviceModel></配置>客户端配置:
<system.serviceModel><绑定><netTcpBinding><绑定名称=NetTcpBinding_IService1"><安全模式=无"/></binding></netTcpBinding></绑定><客户><端点地址=net.tcp://localhost/WebApplication1/Service1.svc";绑定=netTcpBinding"bindingConfiguration=NetTcpBinding_IService1";合同=ServiceReference1.IService1";名称=NetTcpBinding_IService1";/></客户端></system.serviceModel></配置>使用服务的客户端代码(VS2012 使用添加服务引用"为我生成了客户端代理):
private async Task测试任务(){Service1Client 代理 = null;尝试{Console.WriteLine(调用服务");代理 = 新的 Service1Client();return await proxy.DoWorkAsync();}最后{如果(代理.状态!= System.ServiceModel.CommunicationState.Faulted){Console.WriteLine(关闭客户端");代理.关闭();}别的{Console.WriteLine(中止客户端");proxy.Abort();}}}
一切正常:
呼叫服务
关闭客户端
12
但是一旦应用程序终止,服务器就会记录一个异常.我知道我不应该担心这个异常,因为它按预期工作(异常只出现在日志中)并且无论如何都可能发生,以防客户端在调用 .Close()
/.Abort()
.
但是,这是正常行为吗?我的意思是,如果我正确关闭了我的客户端代理,我希望服务器不记录异常(即污染我的日志).我还假设在关闭客户端代理后,客户端和服务器之间仍然建立了一些 TCP 连接(未知状态),因为服务器仅在整个客户端应用程序终止后才记录异常.如果这样的连接仍然打开,这会不会引入意外行为(例如最大连接客户端数)?这真的是预料之中的吗?
我发现了有关该问题的不同主题:
结论是别在意".
有人可以通过一些参考资料确认这一点并解释为什么会抛出这个异常吗?
异常的日志跟踪:
<ExceptionType>System.Net.Sockets.SocketException, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>现有连接被远程主机强行关闭</Message><堆栈跟踪>à System.ServiceModel.Channels.SocketConnection.HandleReceiveAsyncCompleted()à System.ServiceModel.Channels.SocketConnection.OnReceiveAsync(对象发送者,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):现有连接被远程主机强行关闭</ExceptionString><NativeErrorCode>2746</NativeErrorCode></例外>
非常感谢
编辑 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 为每个服务的客户端沟通.
所以现在我的问题是:如果这是正常行为,我可以做些什么来防止我的日志被所有这些异常污染?
要解决此错误,只需关闭 Channel Factory.
private async Task测试任务(){Service1Client 代理 = null;尝试{Console.WriteLine("调用服务");代理 = 新的 Service1Client();return await proxy.DoWorkAsync();}最后{如果(代理.状态!= System.ServiceModel.CommunicationState.Faulted){Console.WriteLine("关闭客户端");proxy.ChannelFactory.Close();代理.关闭();}别的{Console.WriteLine("中止客户端");proxy.Abort();}}}
Please don't close as duplicate until you read the question to the end; I already googled for hours without success.
EDIT: Now I'm convinced it's related to the way WCF caches opened TCP connections (connection pooling). Please take a look at edit #5 at the end of he question.
I basically have a WCF service that uses a netTcpBinding
configuration. Even if I close the client proxy gracefully (see code below), server always logs "System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host
".
I've narrowed down the issue to the most basic WCF example I could write. I'm getting the exception in the logs produced by WCF tracing every-time I close the client application. I'm not getting any exception in my own code though, which means it works as expected and I can't debug anything to see what's going wrong for WCF to add an error in my logs.
Service interface/implementation:
[ServiceContract]
public interface IService1
{
[OperationContract]
string DoWork();
}
...
public class Service1 : IService1
{
public string DoWork()
{
return "12";
}
}
Server-side configuration:
<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>
Client-side 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>
Client-side code that consumes the service (VS2012 generated the client proxy for me using "Add service reference"):
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();
}
}
}
Everything works fine:
But as soon as the application terminates, the server logs an exception. I understand I shouldn't be worried about this exception because it works as expected (exception only appears in logs) and could happen anyway in case the client is terminated abruptly before calling .Close()
/.Abort()
.
But still, is this a normal behavior? I mean, if I correctly closes my client proxy, I expect the server to not log an exception (that is polluting my logs).I also assume some TCP connection is still established between the client and the server (unknown state) after closing the client proxy, because the server only logs the exception after the whole client application terminates. If such a connection is still opened, can't this introduce unexpected behavior (such as max number of connected clients)? Is this really expected?
I found different threads about the issue:
wcf "An existing connection was forcibly closed by the remote host" after closing client
The conclusion would be "don't care about it".
Could someone confirm that with some references and explain why this exception is thrown anyway?
EDIT:
Log trace of the exception:
<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>
Thanks a lot
EDIT 2: I have the same problem when hosting the service in IIS or when it's self-hosted in a Windows service
EDIT 3: here's a full example to reproduce the issue: http://speedy.sh/ENB59/wcf-test.zip
EDIT 4:
I tried to monitor what is actually happening under the hood with the TCP connection WCF establishes for me.
After closing the client proxy, I still see an opened TCP connection to my server:
I assume this is related to the TCP connection to be cached for a future re-use (i.e. connection pooling), because opening a new connection to the server (after the first client proxy has been closed) doesn't create a new TCP connection. If I call Console.WriteLine(new Test().TestTask().Result);
twice in my application, I still only see one opened TCP connection.
I also noted this connection dies because of a timeout if I wait too long after closing the client channel.
EDIT 5: OK, I found documentation on MSDN about that connection pooling:
So now my question would be: if that's a normal behavior, what could I do to prevent my log to be polluted with all those exceptions?
To resolve this error, simply close the Channel Factory as well.
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.ChannelFactory.Close();
proxy.Close();
}
else
{
Console.WriteLine("Aborting client");
proxy.Abort();
}
}
}
这篇关于如何正确关闭客户端代理(现有连接被远程主机强行关闭)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!