17

就在最近,我开始研究 WCF 流的一个棘手问题,如果客户端在两次发送到服务器之间等待的时间超过 130 秒,就会产生 CommunicationException。

这是完整的例外:

System.ServiceModel.CommunicationException was unhandled by user code
  HResult=-2146233087
  Message=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. Local socket timeout was '23:59:59.9110000'.
  Source=mscorlib
  StackTrace:
    Server stack trace: 
       at System.ServiceModel.Channels.HttpOutput.WebRequestHttpOutput.WebRequestOutputStream.Write(Byte[] buffer, Int32 offset, Int32 count)
       at System.IO.BufferedStream.Write(Byte[] array, Int32 offset, Int32 count)
       at System.Xml.XmlStreamNodeWriter.FlushBuffer()
       at System.Xml.XmlStreamNodeWriter.GetBuffer(Int32 count, Int32& offset)
       at System.Xml.XmlUTF8NodeWriter.InternalWriteBase64Text(Byte[] buffer, Int32 offset, Int32 count)
       at System.Xml.XmlBaseWriter.WriteBase64(Byte[] buffer, Int32 offset, Int32 count)
       at System.Xml.XmlDictionaryWriter.WriteValue(IStreamProvider value)
       at System.ServiceModel.Dispatcher.StreamFormatter.Serialize(XmlDictionaryWriter writer, Object[] parameters, Object returnValue)
       at System.ServiceModel.Dispatcher.OperationFormatter.OperationFormatterMessage.OperationFormatterBodyWriter.OnWriteBodyContents(XmlDictionaryWriter writer)
       at System.ServiceModel.Channels.Message.OnWriteMessage(XmlDictionaryWriter writer)
       at System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessage(Message message, Stream stream)
       at System.ServiceModel.Channels.HttpOutput.WriteStreamedMessage(TimeSpan timeout)
       at System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout)
       at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.SendRequest(Message message, TimeSpan timeout)
       at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
       at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
       at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
       at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
    Exception rethrown at [0]: 
       at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       at WcfService.IStreamingService.SendStream(MyStreamUpRequest request)
       at Client.Program.<Main>b__0() in c:\Users\jpierson\Documents\Visual Studio 2012\Projects\WcfStreamingTest\Client\Program.cs:line 44
       at System.Threading.Tasks.Task.Execute()
  InnerException: System.IO.IOException
       HResult=-2146232800
       Message=Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host.
       Source=System
       StackTrace:
            at System.Net.Sockets.NetworkStream.MultipleWrite(BufferOffsetSize[] buffers)
            at System.Net.ConnectStream.InternalWrite(Boolean async, Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
            at System.Net.ConnectStream.Write(Byte[] buffer, Int32 offset, Int32 size)
            at System.ServiceModel.Channels.BytesReadPositionStream.Write(Byte[] buffer, Int32 offset, Int32 count)
            at System.ServiceModel.Channels.HttpOutput.WebRequestHttpOutput.WebRequestOutputStream.Write(Byte[] buffer, Int32 offset, Int32 count)
       InnerException: System.Net.Sockets.SocketException
            HResult=-2147467259
            Message=An existing connection was forcibly closed by the remote host
            Source=System
            ErrorCode=10054
            NativeErrorCode=10054
            StackTrace:
                 at System.Net.Sockets.Socket.MultipleSend(BufferOffsetSize[] buffers, SocketFlags socketFlags)
                 at System.Net.Sockets.NetworkStream.MultipleWrite(BufferOffsetSize[] buffers)
            InnerException: 

由于连接不活动,服务器似乎过早地关闭了连接。如果我改为向服务器发送一个脉冲,甚至一次一个字节,那么我永远不会得到这个异常,我可以继续无限期地传输数据。我构建了一个非常简单的示例应用程序来演示这一点,它使用带有 Streamed transferMode 的 basicHttpBinding,我从客户端上的自定义流实现中插入了一个人工延迟,延迟 130 秒。这模拟了类似于缓冲区运行不足的情况,在这种情况下,我在客户端的服务调用中提供的流没有足够快地将数据提供给 WCF 基础结构,无法满足某种似乎在 130 秒左右的无法识别的超时值标记。

使用 WCF 服务跟踪工具,我可以找到一个 HttpException,并显示一条消息:“客户端已断开连接,因为基础请求已完成。不再有可用的 HttpContext。”

从 IIS Express 跟踪日志文件中,我看到一个条目显示“由于线程退出或应用程序请求,I/O 操作已中止。(0x800703e3)”

我已将服务器和客户端超时配置为使用远超过 130 秒标记的值来排除它们。我已经在 IIS Express 中尝试了 idleTimeout 以及许多与 ASP.NET 相关的超时值,以便发现这个问题来自哪里,但到目前为止还没有运气。到目前为止,我能找到的最佳信息是开发人员在 FireFox 问题跟踪器中的评论,该评论描述了在 WCF 体系结构之外工作的类似问题。出于这个原因,我猜这个问题可能更具体地与 IIS7 或 Windows Server 相关。

服务器 Web.config 上的自定义绑定

<binding name="myHttpBindingConfiguration"
         closeTimeout="02:00:00"
         openTimeout="02:00:00"
         receiveTimeout="02:00:00"
         sendTimeout="02:00:00">
  <textMessageEncoding messageVersion="Soap11" />
  <httpTransport maxBufferSize="65536"                        
                 maxReceivedMessageSize="2147483647"
                 maxBufferPoolSize="2147483647"
                 transferMode="Streamed" />
</binding>

代码中的客户端配置:

    var binding = new BasicHttpBinding();
    binding.MaxReceivedMessageSize = _maxReceivedMessageSize;
    binding.MaxBufferSize = 65536;
    binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
    binding.ReaderQuotas.MaxArrayLength = int.MaxValue;
    binding.TransferMode = TransferMode.Streamed;
    binding.ReceiveTimeout = TimeSpan.FromDays(1);
    binding.OpenTimeout = TimeSpan.FromDays(1);
    binding.SendTimeout = TimeSpan.FromDays(1);
    binding.CloseTimeout = TimeSpan.FromDays(1);

为了响应 wals 的想法,尝试通过自行托管我的服务来查看我是否得到任何不同的结果,我想补充一点,我这样做了,发现我得到的结果与在 IIS 中托管时相同。这是什么意思?我的猜测是,这意味着问题出在 WCF 或 Windows 的底层网络基础结构中。我使用的是 Windows 7 64 位,我们通过运行各种客户端并在 Windows 2008 Server 上运行服务部分发现了这个问题。

2013-01-15 更新

一旦我意识到 WCF 在 Windows 7 上的自托管场景中使用 HTTP.sys,多亏了 DarkWanderer,我发现了一些新线索。这让我研究了我可以为 HTTP.sys 配置什么以及人们报告的问题类型对于 HTTP.sys,这听起来与我所经历的相似。这将我带到位于C:\Windows\System32\LogFiles\HTTPERR\httperr1.log的日志文件,该文件似乎记录了 HTTP.sys 的特定类型的 HTTP 问题。在此日志中,我每次运行测试时都会看到以下类型的日志条目。

2013-01-15 17:17:12 127.0.0.1 59111 127.0.0.1 52733 HTTP/1.1 POST /StreamingService.svc - - Timer_EntityBody -

因此,要找出哪些条件可能导致Timer_EntityBody错误,以及 IIS7 或其他地方的哪些设置可能会影响何时以及是否发生该错误。

来自官方 IIS 网站

在请求实体正文到达之前连接已过期。当明确请求具有实体主体时,HTTP API 会打开 Timer_EntityBody 计时器。最初,此计时器的限制设置为 connectionTimeout 值。每次在此请求上收到另一个数据指示时,HTTP API 都会重置计时器,以便为连接提供更多分钟,如 connectionTimeout 属性中指定的。

尝试按照上面的参考在 IIS Express 的 applicationhost.config 中建议修改 connectionTimeout 属性似乎没有任何区别。也许 IIS Express 会忽略此配置并在内部使用硬编码值?我自己尝试了一些东西,我发现添加了新的 netsh http 命令来显示和添加超时值,所以我想出了以下命令,但不幸的是,这样做似乎对这个错误也没有任何影响。

netsh http 添加超时 timeouttype=IdleConnectionTimeout value=300

4

5 回答 5

16

事实证明,这个问题是由 HTTP.sys 使用的连接超时值引起的,并且可以通过 IIS 管理器通过单个站点的高级设置来指定。默认情况下,此值配置为在 120 秒内未收到标头和正文时使连接超时。如果接收到来自正文的数据脉冲,则服务器在超时值内重新启动计时器(Timer_EntityBody),然后重置计时器以等待其他数据。

IIS 中的连接超时设置

这就像有关Timer_EntityBodyconnectionTimeout的文档所指定的那样,但是很难确定,因为无论文档怎么说,IIS Express 似乎都忽略了applicationhost.config中的限制元素中指定的 connectionTimeout 值。为了确定这一点,我必须在我的开发机器上安装完整版本的 IIS,并在将我的网站托管在那里之后修改上面的设置。

由于我们在 Windows 2008 上的 IIS 下托管实际服务,因此上述解决方案对我有用,但问题仍然是在您是自托管的情况下如何正确修改连接超时值。

于 2013-01-15T23:45:32.310 回答
2

从错误来看:

套接字连接被中止。这可能是由于处理您的消息时出错或远程主机超出接收超时,或者是潜在的网络资源问题造成的。本地套接字超时为“23:59:59.9110000”

看起来这是一个简单的 TCP 超时。

您可以通过将应用程序作为自托管运行来验证它,然后在控制台中运行此命令:

netstat -no |find "xxxxx"

其中 xxxxx 是您的服务器进程的 PID。此命令将显示您的服务器已建立的连接并每秒刷新一次。

尝试与客户端连接,看看会发生什么。大约 100-120 秒后,您很可能会在连接上看到“CLOSE_WAIT”或“TIME_WAIT”——这意味着它由于超时而中止。

这可以通过将以下内容添加到您的配置来解决:

<httpTransport maxBufferSize="65536"                        
             maxReceivedMessageSize="2147483647"
             maxBufferPoolSize="2147483647"
             transferMode="Streamed"
             keepAliveEnabled="true" /> <!-- Here -->

参数说明here

于 2013-01-15T09:50:00.357 回答
1

可能是长镜头,但是...检查您的 IIS 应用程序池是否启用了 Ping。它在高级设置中,流程模型分组。

于 2013-01-10T21:03:59.487 回答
0

试试这个,对我来说它解决了这个问题。问题是底层内核 http.sys 有它自己的超时,它会中断连接。

http://mmmreddy.wordpress.com/2013/07/11/wcf-use-of-http-transport-sharing-persistent-tcp-sessions/

netsh http add timeout timeouttype=idleconnectiontimeout value=120
于 2013-08-27T15:48:28.057 回答
0

你考虑过 http://support.microsoft.com/kb/946086吗?

我在我的 ISAPI-Extensions 中观察到了这样的流中断。根据此支持说明关闭 IIS 7 中的缓冲后,一切正常。

于 2013-10-10T21:12:27.277 回答