c# - 关闭客户端应用程序: existing connection was forcibly closed by the remote host时在服务器上记录ServiceModel错误

标签 c# wcf sockets trace

我有一个自托管的WCF服务,还有几个客户端进程...一切正常,客户端启动,进行多个服务调用,然后退出。但是,在服务器上,每次客户端应用程序关闭时,我的错误日志(我从System.ServiceModel转发错误级别跟踪消息的位置)都会有一个条目(这与服务方法调用不符)。

我在.NET 4.5上使用自定义tcp绑定(bind)...

 <bindings>
  <customBinding>
    <binding name="tcp">
      <security authenticationMode="SecureConversation" />
      <binaryMessageEncoding compressionFormat="GZip" />
      <tcpTransport />
    </binding>
  </customBinding>

客户端派生自ClientBase,我确实在客户端上调用Close()而不出现问题。 ClientBase的多个实例已创建,并且在操作期间已关闭且没有错误。

我的猜测是客户端将套接字保持打开状态以供重用(明智的优化)。然后在应用程序导出处,该套接字已断开。

这是否代表我应该解决的错误?如果它不是真正的“错误”,我是否仍可以某种方式避免这种情况,以免在我的错误日志中放置垃圾内容?

客户端绑定(bind)配置与服务器完全相同(自然)。这是我的调用代码...请注意,我使用了this question中的ServiceHelper类。
using (var helper = new ServiceHelper<ARAutomatchServiceClient, ServiceContracts.IARAutomatchService>())
{
    return await helper.Proxy.GetBatchesAsync(startDate, DateTime.Today.AddDays(5));
}

具体来说,我看到的服务器上的“错误”级别跟踪事件包含以下消息(为简洁起见,清除了堆栈跟踪和其他元素):

System.ServiceModel Error: 131075 :

System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue.

System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host NativeErrorCode: 2746

最佳答案

我在ServiceModel跟踪日志中看到的所有不需要的错误消息的来源都来自服务器上超时的连接池中的连接,或者来自客户端进程退出时客户端断开连接。

如果服务器上的共享连接超时,则在超时时立即在服务器上写入一些跟踪消息,然后在开始下一个操作时在客户端上写入一些跟踪消息。这些是“错误”级别的跟踪消息。

如果客户端进程在关闭连接之前退出,则在客户端进程退出时,您会立即在服务器上收到不同的错误级别跟踪消息。

这些是错误级别跟踪消息的事实尤其令人讨厌,因为即使在生产环境中,我也通常会记录这些消息……但是看来这些消息通常应该忽略不计,因为这是常规连接池连接超时的结果。

Microsoft已在此处解决了池连接关闭问题的一种描述。

http://support.microsoft.com/kb/2607014

上面的文章建议ServiceModel处理异常,当您在TraceLogs中看到它时,可以放心地忽略它。该特定情况被记录为“信息”级别的事件,与我实际记录的“错误”级别的事件一样,它也不会给我带来太大的麻烦。我试图从日志中“过滤”这些消息,但这非常困难。

自然,您可以通过在池中的连接(在服务器上)超时之前显式关闭池中的连接(在客户端上)来完全避免这种情况。为了让客户端关闭连接池中的连接(对于使用tcp传输的WCF绑定(bind)),我知道起作用的唯一事情是显式关闭ChannelFactory实例。实际上,如果您不缓存这些实例(并且不使用通常为您缓存它们的ClientBase),那么您将没有任何问题!如果您确实想缓存ChannelFactory实例,则至少应在应用程序退出之前显式关闭它们,这并不是我见过任何建议。在客户端应用程序退出之前关闭它们将处理丢弃的套接字的主要来源之一,这些套接字将作为服务器上的ServiceModel“Error”跟踪记录下来。

这是关闭 channel 工厂的一些代码:

try
{
    if (channelFactory != null)
    {
        if (channelFactory.State != CommunicationState.Faulted)
        {
            channelFactory.Close();
        }
        else
        {
            channelFactory.Abort();
        }
    }
}
catch (CommunicationException)
{
    channelFactory.Abort();
}
catch (TimeoutException)
{
    channelFactory.Abort();
}
catch (Exception)
{
    channelFactory.Abort();
    throw;
}
finally
{
    channelFactory= null;
}

只是在您调用该代码的地方有些棘手。在内部,我将其安排在AppDomain.ProcessExit中以“确保”它被调用,但同时建议我的服务基类的使用者记住比AppDomain.ProcessExit早某个时间明确地调用“关闭缓存的工厂”代码,因为ProcessExit处理程序仅限大约3秒钟即可完成。当然,进程可能会突然关闭并且永远不会调用此命令,但这是可以的,只要它发生的时间不足以淹没您的服务器日志即可。

就池连接超时而言...您可以将服务器上的TCP传输“ConnectionPool”超时值提高到很高(几天),在某些情况下可能还可以。这至少将使连接在服务器上超时的可能性很小或很少。请注意,在客户端上具有较短的超时值似乎不会以任何方式影响这种情况,因此该设置也可以保留为默认值。 (原因:下一次客户端需要连接时,客户端的连接将被视为超时,但是到此时服务器将已经超时并记录错误,否则,客户端将关闭并创建新的连接连接并重新启动服务器超时时间。不过,简单地使用连接也会重新启动服务器超时时间。)

同样,无论客户端设置如何,服务器上的连接池超时必须足够高,以覆盖客户端不 Activity 的时间段。您可以通过减小客户端上池的大小(maxOutboundConnectionsPerEndpoint)来进一步降低池化连接超时的可能性,以使客户端打开的连接不会超过实际需要的数量,从而使它们保持闲置状态,并最终对时间进行调整-在服务器上。

必须使用内置绑定(bind)的代码(如netTcpBinding)来为绑定(bind)配置连接池。对于自定义绑定(bind),您可以按以下配置进行配置(在这里,我将服务器设置为2天超时,并且仅缓冲100个连接):
  <customBinding>
    <binding name="tcp">
      <security authenticationMode="SecureConversation"/>
      <binaryMessageEncoding compressionFormat="GZip"/>
      <tcpTransport>
        <connectionPoolSettings idleTimeout="2.00:00:00"
                                maxOutboundConnectionsPerEndpoint="100" />
      </tcpTransport>
    </binding>
  </customBinding>

这两种方法一起使用(提高服务器端超时并在客户端退出时关闭ChannelFactory实例)可能会解决您的问题,或者至少可以大大减少“可以忽略的安全”消息的数量。确保连接池的服务器超时至少与客户端的超时时间相同,以确保连接一旦在服务器上超时就首先在客户端上超时(这在ServiceModel中似乎得到了更合理的处理,跟踪消息较少,并且恰恰是上面链接的知识库文章中提到的情况)。

在服务器中,理想情况下,您希望有足够的maxOutboudnConnectionsPerEndpoint服务(客户端数量)x(它们的池连接数量)。否则,您可能最终导致服务器上的池超限,从而发出警告级别的跟踪事件。还算不错如果在新客户端尝试连接时服务器的池上没有可用的连接,则会在客户端和服务器上生成一堆事件。在所有这些情况下(即使服务器上的池不断溢出),WCF也会恢复运行,只是不是最佳状态。至少以我的经验,这是可能的...如果新连接的“LeaseTime”超时,等待服务器连接池位置打开(默认值为5分钟),那么它会完全失败吗?没有把握...

最后的建议可能是定期关闭ChannelFactory对象并回收缓存的副本。假设客户端在ChannelFactory实例回收期间没有尝试完全使用服务,这可能对性能仅产生有限的影响。例如,您可以将缓存的ChannelFactory实例在创建后安排5分钟的回收时间(而不是在上次使用之后),因为它可能有多个池连接,其中一个池已经有一段时间没有使用了。然后将服务器上的连接池超时设置为10分钟左右。但是请确保在ChannelFactory回收期间服务器超时很大,因为当您回收ChannelFactory时,您可能必须等待直到挂起的操作完成(同时一些未使用的池化连接可能刚刚在服务器上超时)。

所有这些都是微优化,可能不值得做……但是,如果您在生产中记录错误级别的ServiceModel跟踪事件,则可能要执行某些操作(即使它正在禁用连接池或ChannelFactory缓存),或者您的日志可能会充满“可以忽略”的错误。

关于c# - 关闭客户端应用程序: existing connection was forcibly closed by the remote host时在服务器上记录ServiceModel错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19817655/

相关文章:

javascript - 如何创建基于网络服务的计时器?

c# - Lambda 表达式中的动态 Where 子句

c# - Json.net 自定义 JsonConvert for Dictionary<string,object>[]

WCF 发布错误 : Metadata contains a reference that cannot be resolved:

java - 是否有可以与 Microsoft WCF 服务进行二进制通信的 Java API?

linux原始套接字编程问题

c - Linux : TCP socket programming over multiple ethernet ports

c - Windows 工作站不可知设置广播客户端的正确方法是什么?

c# - 如何在基于 Roslyn 的脚本中使用 System.IO.FileSystem?

c# - 从数组中删除,镜像(奇怪)行为