本文可帮助你解决使用WebExceptionStatus
类时引发不同System.Net.HttpWebRequest
问题。
原始产品版本: .Net Framework
原始 KB 数: 2007873
症状
使用 System.Net.HttpWebRequest
Microsoft .Net Framework 类将超文本传输协议(HTTP)或超文本传输协议安全(HTTPS)请求发送到服务器。 此请求需要一些时间才能从服务器接收响应。 在此等待期间,如果手动增加系统时钟时间,或者系统时钟滞后,然后 Windows 时间服务会调整到实际的本地时间,则会出现以下情况之一:
对于通过纯文本 HTTP 发送的请求,该 System.Net.HttpWebRequest
类将引发以下异常:
请求已中止:操作已超时。
此外, Status
引发 WebException
的属性将指示值 WebExceptionStatus.Timeout
。
对于通过 HTTPS 发送的请求,该 System.Net.HttpWebRequest
类将引发以下异常之一:
基础连接已关闭:接收时出现意外错误。
此外, Status
引发 WebException
的属性将指示值 WebExceptionStatus.ReceiveFailure
。
或
基础连接已关闭:服务器关闭了应保持活动状态的连接。
此外, Status
引发 WebException
的属性将指示值 WebExceptionStatus.KeepAliveFailure
。
在上述所有情况下,捕获 InnerException
的属性。 如果捕获 WebException
并引用属性 WebException.InnerException.InnerException
,你会注意到,对于上述所有情况,字符串 Message
将指示:
连接尝试因连接的服务器在经过一段时间后未能正确响应而失败,或建立的连接因连接的主机未响应而失败。
此消息是 Winsock 错误代码 10060 = WSAETIMEDOUT 的详细解释。
因此,当系统时间手动增加时,Winsock 会正确引发超时错误 10060,但它被包装为安全套接字层 (SSL) 和非 SSL 请求的不同异常类型。
在系统时间不被篡改的正常超时情况下,SSL 和非 SSL 方案将正确反映 WebExceptionStatus.Timeout
状态并引发常见异常: 操作已超时。
原因
通过 SSL 或非 SSL 发出请求时,类 System.Net.ServicePointManager
会将请求分配给内部连接,最终将进行 Winsock 连接。 对于 SSL 请求,此请求或连接通过另一个内部 SSL/TLS 类,负责加密或解密数据。 对于非 SSL 连接,此内部 SSL/TLS 类根本不涉及。
修改时间并在 Winsock 层遇到异常时,此错误现在需要从 Winsock 向上移动到应用程序层。 对于非 SSL 连接,此异常由内部连接类直接捕获,但对于 SSL 请求,此错误由内部 SSL/TLS 类处理。 此类将此非 SSL 错误视为 ReceiveFailure
或 KeepAliveFailure
因此具有不同的异常状态,而对于非 SSL 连接,错误将正确强制转换,因为它由其他类处理。
状态
此行为是设计造成的。
决议
为了解决此特殊条件下引发的异常类型的这种差异,系统时间被篡改,应用程序需要捕获 WebException
并引用 WebException.InnerException.InnerException.Message
该属性。
Message
如果字符串等于 10060 = WSAETIMEDOUT 的 winsock 详细错误,则可以考虑或ReceiveFailure
视为常规超时,而不将其KeepAliveFailure
视为ReceiveFailure
或。KeepAliveFailure
应用程序在执行框架英文版时catch()
WebException
,可以使用以下解决方法。 对于框架的本地化版本,需要根据语言本地化调整以下解决方法。
重要
此示例代码按原样提供,仅用于示例目的。 它提供没有保证,并授予任何权利。
try
{
......
}
catch (WebException oWEx)
{
WebExceptionStatus oStatus = oWEx.Status;
String strTimeoutErrorMessage = "A connection attempt failed because the connected party did not properly respond "
+ "after a period of time, or established connection failed because connected host has failed to respond";
switch (oStatus)
{
case WebExceptionStatus.KeepAliveFailure:
if ((oWEx.InnerException != null) && (oWEx.InnerException.InnerException != null)
&& oWEx.InnerException.InnerException.Message.ToString().Equals(strTimeoutErrorMessage, StringComparison.CurrentCultureIgnoreCase))
{ //----------------------------------------------------------------------
// This is Timeout Error which is wrongly thrown as a ReceiveFailure for
// SSL requests under this special condition.
//
// Handle this as a Timeout Error
//----------------------------------------------------------------------
}
else
{
//----------------------------------------------------------------------
// This is truly a KeepAliveFailure.
//----------------------------------------------------------------------
}
break;
case WebExceptionStatus.Timeout:
//----------------------------------------------------------------------
// This is a Timeout.
//----------------------------------------------------------------------
break;
case WebExceptionStatus.ReceiveFailure:
if ((oWEx.InnerException != null)
&& (oWEx.InnerException.InnerException != null)
&& oWEx.InnerException.InnerException.Message.ToString ().Equals (strTimeoutErrorMessage, StringComparison.CurrentCultureIgnoreCase))
{ //----------------------------------------------------------------------
// This is Timeout Error which is wrongly thrown as a ReceiveFailure for
// SSL requests under this special condition.
//
// Handle this as a Timeout Error
//----------------------------------------------------------------------
}
else
{ //----------------------------------------------------------------------
// This is truly a ReceiveFailure.
//----------------------------------------------------------------------
}
break;
default:
//----------------------------------------------------------------------
// This is some other Exception
//----------------------------------------------------------------------
break;
}
}