我正在开发一个链接检查器,一般来说我可以执行HEAD
个请求,但有些网站似乎禁用了这个动词,所以一旦失败我还需要执行GET
请求(加倍)检查链接真的死了)
我使用以下代码作为我的链接测试器:
public class ValidateResult
{
public HttpStatusCode? StatusCode { get; set; }
public Uri RedirectResult { get; set; }
public WebExceptionStatus? WebExceptionStatus { get; set; }
}
public ValidateResult Validate(Uri uri, bool useHeadMethod = true,
bool enableKeepAlive = false, int timeoutSeconds = 30)
{
ValidateResult result = new ValidateResult();
HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
if (useHeadMethod)
{
request.Method = "HEAD";
}
else
{
request.Method = "GET";
}
// always compress, if you get back a 404 from a HEAD it can be quite big.
request.AutomaticDecompression = DecompressionMethods.GZip;
request.AllowAutoRedirect = false;
request.UserAgent = UserAgentString;
request.Timeout = timeoutSeconds * 1000;
request.KeepAlive = enableKeepAlive;
HttpWebResponse response = null;
try
{
response = request.GetResponse() as HttpWebResponse;
result.StatusCode = response.StatusCode;
if (response.StatusCode == HttpStatusCode.Redirect ||
response.StatusCode == HttpStatusCode.MovedPermanently ||
response.StatusCode == HttpStatusCode.SeeOther)
{
try
{
Uri targetUri = new Uri(Uri, response.Headers["Location"]);
var scheme = targetUri.Scheme.ToLower();
if (scheme == "http" || scheme == "https")
{
result.RedirectResult = targetUri;
}
else
{
// this little gem was born out of http://tinyurl.com/18r
// redirecting to about:blank
result.StatusCode = HttpStatusCode.SwitchingProtocols;
result.WebExceptionStatus = null;
}
}
catch (UriFormatException)
{
// another gem... people sometimes redirect to http://nonsense:port/yay
result.StatusCode = HttpStatusCode.SwitchingProtocols;
result.WebExceptionStatus = WebExceptionStatus.NameResolutionFailure;
}
}
}
catch (WebException ex)
{
result.WebExceptionStatus = ex.Status;
response = ex.Response as HttpWebResponse;
if (response != null)
{
result.StatusCode = response.StatusCode;
}
}
finally
{
if (response != null)
{
response.Close();
}
}
return result;
}
这一切都很好,花花公子。除了当我执行GET
请求时,整个有效负载被下载(我在wireshark中观看了这个)。
有没有办法配置基础ServicePoint
或HttpWebRequest
不要缓冲或急切加载响应主体?
(如果我手动编码,我会将TCP接收窗口设置得很低,然后只抓取足够的数据包来获取Headers,一旦我有足够的信息就停止对TCP数据包进行处理。)
对于那些想知道这意味着什么的人来说,我不想下载一个40k 404,当我得到一个404,这样做几十万在网络上是昂贵的
答案 0 :(得分:8)
当您执行GET时,服务器将开始从文件的开头发送数据到结尾。除非你打断它。当然,以10 Mb /秒的速度,这将是每秒兆字节,所以如果文件很小,你将得到整个东西。您可以通过几种方式最小化实际下载量。
首先,您可以在收到回复之后和致电request.Abort
之前致电response.close
。这将确保底层代码在关闭响应之前不会尝试下载整个内容。这是否有助于小文件,我不知道。我知道它会阻止您的应用程序在尝试下载多GB文件时挂起。
您可以做的另一件事是请求范围,而不是整个文件。请参阅AddRange方法及其重载。例如,您可以编写request.AddRange(512)
,它只下载文件的前512个字节。当然,这取决于支持范围查询的服务器。大部分都做。但是,大多数人也支持HEAD请求。
您可能最终必须编写一个按顺序尝试的方法:
request.Abort
返回后GetResponse
。答案 1 :(得分:1)
如果您正在使用GET请求,您将收到消息正文,无论您是否愿意。无论您是否从套接字读取数据,数据仍将传输到您的端点。数据将在RecvQ中排队等待被选中。
为此,如果可能的话,你真的应该使用“HEAD”请求,这将使你免于消息体。
答案 2 :(得分:0)
难道您不能使用WebClient打开流并只读取您需要的几个字节吗?
using (var client = new WebClient())
{
using (var stream = client.OpenRead(uri))
{
const int chunkSize = 100;
var buffer = new byte[chunkSize];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
//check response here
}
}
}
我不确定WebClient如何在内部打开流。但它似乎允许部分阅读数据。