一、GetResponse操作超时问题
最近在写一个爬网页的程序,可是每次才下载20几个页面就报“操作超时 ”的问题,上“度娘”、“谷哥”去查了好久,有的人说每次用完HttpWebRequest 要用request.Abort()将请求关闭,有的说要设置超时时间“request.Timeout = 200000”,有的说有可能是服务器连接过多,要少点并发,要加“DefaultConnectionLimit=50”,还有的人说可能是服务器将程序的请求当成异常请求直接拒了,所以要设置“request.Method = "GET";request.UserAgent = "Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox/19.0";”,可是这些我都做了,还是有提示“操作超时”,怎么办呢?于是,我在整合以上配置的同时,加上了响应超时判断,“ if (response.StatusCode == HttpStatusCode.RequestTimeout)”,最后终于可以了,为了让更多人受益,所以把结果分享给大家。我的代码如下:
request = (HttpWebRequest)WebRequest.Create(m_uri);
request.KeepAlive = false;
request.Method = "GET";
request.UserAgent = "Mozilla/5.0 (Windows NT 5.1; rv:19.0) Gecko/20100101 Firefox/19.0";
//如果服务器返回错误,他还会继续再去请求,不会使用之前的错误数据,做返回数据
request.ServicePoint.Expect100Continue = false;
request.Timeout = 200000;
ServicePointManager.DefaultConnectionLimit = 20;
<span style="white-space:pre"> </span>//关键可能就是这个位置
Monitor.Enter(this);
using (response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.RequestTimeout)
{
Thread.Sleep(1000);
return null;
}
else
{
stream = response.GetResponseStream();
if (!response.ContentType.ToLower().StartsWith("text/"))
{
SaveBinaryFile(response);
return null;
}
string buffer = "", line;
reader = new StreamReader(stream, System.Text.Encoding.GetEncoding("gb2312"));
while ((line = reader.ReadLine()) != null)
{
buffer += line + "\r\n";
}
request.Abort();
return buffer;
}
}
在request 请求前面加上
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3; 试试
三、
随着REST风格的流行,直接通过 HttpWebRequest 进行服务调用的客户端应用越来越多。这里总结一些可能需要费时调查的经验,希望能帮助大家。
1. 用完的HttpWebRequest要Abort()或者要把 Response.Close()
否则会导致请求Timeout。 (HttpWebRequest.Method默认是GET)
static void Main( string [] args)
{
for ( int i = 0; i < 10; i++)
{
Console.Write( "[{0}] Request - " , i + 1);
TryGet( "https://login.live.com/" );
}
Console.Read();
}
static void TryGet( object obj)
{
try
{
HttpWebRequest webReq = null ;
string url = ( string )obj;
webReq = (HttpWebRequest)HttpWebRequest.Create(url);
webReq.Timeout = 20 * 1000;
var resp = webReq.GetResponse() as HttpWebResponse;
resp.Close();
Console.WriteLine( "Get Response StatusCode: {0}({1})" ,
resp.StatusCode, ( int )resp.StatusCode);
}
catch (WebException we)
{
Console.WriteLine( "Get Response StatusCode: {0}({1})" , we.Status, ( int )we.Status);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
上面的代码,会从第3次Request开始出现Timeout,因为GetResponse 后 Stream打开未关闭。
解决方法:上面的代码中加上 resp.Close(); 或者 webReq.Abort(); 就能解决。
2. 多线程中调用 HttpWebRequest 时,需要设置 ServicePointManager.DefaultConnectionLimit 数(默认连接数是 2)。
当多线程请求时,同时的连接数超过Limit时,GetResponse会抛出 Timeout WebException。
- // 用多线程同时发出4个请求
- WaitCallback methodTarget = new WaitCallback(TryGet);
- ThreadPool.QueueUserWorkItem(methodTarget, "https://login.live.com/" );
- ThreadPool.QueueUserWorkItem(methodTarget, "https://login.live.com/" );
- ThreadPool.QueueUserWorkItem(methodTarget, "https://login.live.com/" );
- ThreadPool.QueueUserWorkItem(methodTarget, "https://login.live.com/" );
解决方法:在GetResponse()之前设置 ServicePointManager.DefaultConnectionLimit = 100;
3. 当请求一个基于SSL的服务时,默认的验证行为都在 ServicePointManager 定义:
ServicePointManager.CheckCertificateRevocationList = true;
如果请求的服务端证书没有第三方的认证支持,则请求会失败,如果要完全信任服务端证书,则可以将
CheckCertificateRevocationList 设为 false。
4. 可以在 <system.net> 配置节中配置 HttpWebRequest 的属性,包括 WebProxy
- <system.net>
- <connectionManagement>
- </connectionManagement>
- <defaultProxy>
- <proxy proxyaddress= "http://xxx.xxx.xxx.xxx:xxx" bypassonlocal= "False" />
- </defaultProxy>
- <settings>
- <httpWebRequest useUnsafeHeaderParsing= "true" />
- <servicePointManager checkCertificateName= "true"
- checkCertificateRevocationList= "true"
- enableDnsRoundRobin= "true"
- expect100Continue= "true"
- useNagleAlgorithm= "true" />
- </settings>
- </system.net>
错误提示的Message为:
基础连接已经关闭: 发送时发生错误。
InnerException为:
从传输流收到意外的 EOF 或 0 个字节。
试了网上的N种方法,以下是本次的解决方案:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
把网上找到的解决方案列一下,没准就能解决了你的问题
以下是网上找到的解决方案:
1. request.ProtocolVersion = HttpVersion.Version10;
2. ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
3.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
4.换个UserAgent试试
5.在App.config中添加以下内容:
<system.net>
<settings>
<httpWebRequest useUnsafeHeaderParsing="true"/>
</settings>
</system.net>