C# 如何在不下载内容的情况下执行GET请求?
我正在使用链接检查器,通常我可以执行C# 如何在不下载内容的情况下执行GET请求?,c#,.net,httpwebrequest,servicepoint,C#,.net,Httpwebrequest,Servicepoint,我正在使用链接检查器,通常我可以执行HEAD请求,但是有些站点似乎禁用了这个动词,因此如果失败,我还需要执行GET请求(再次检查链接是否真的死了) 我使用以下代码作为我的链接检测仪: public class ValidateResult { public HttpStatusCode? StatusCode { get; set; } public Uri RedirectResult { get; set; } public WebExceptionStatus? WebExce
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接收窗口设置得很低,然后只抓取足够的数据包来获取标题,一旦我有足够的信息,就停止确认TCP数据包。)
对于那些想知道这意味着什么的人,当我得到一个404时,我不想下载一个40k 404,在网络上这样做几十万次是很昂贵的难道你不能使用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如何在内部打开流。但它似乎允许部分读取数据。如果使用GET请求,则无论您是否愿意,都将收到消息正文。无论是否从套接字读取数据,数据仍将传输到端点。数据将在RecvQ中排队等待被选中
为此,如果可能的话,您确实应该使用“HEAD”请求,这将节省您的消息正文。当您执行GET时,服务器将开始从文件的开头到结尾发送数据。除非你打断它。诚然,在10 Mb/秒的速度下,这将是每秒1兆字节,因此如果文件很小,您将得到整个文件。您可以通过两种方式最大限度地减少实际下载量 首先,您可以在收到响应后调用
request.Abort
,然后再调用response.close
。这将确保底层代码在关闭响应之前不会尝试下载整个内容。我不知道这对小文件是否有帮助。我知道当你的应用程序试图下载一个千兆字节的文件时,它会阻止你的应用程序挂起
您可以做的另一件事是请求一个范围,而不是整个文件。请参阅该方法及其重载。例如,您可以编写
request.AddRange(512)
,它只下载文件的前512字节。当然,这取决于支持范围查询的服务器。大多数都是这样。但是,大多数人也支持HEAD请求
您可能最终不得不编写一个按顺序进行尝试的方法:
- 试着做一个头部请求。如果这样做有效(即不返回500),那么您就完成了
- 尝试使用范围查询获取。如果没有返回500,那么你就完了
- 使用
请求执行常规GET。在
返回后中止GetResponse
Content range
对于包含该内容的http 1.1服务器可能没问题,但如果您得到404,它仍然会被完全发回。我很同情您,因为我写了一个链接检查器,并且遇到了同样的问题。某些众所周知的领域,如维基百科和IMDB,莫名其妙地拒绝了HEAD请求。恐怕从来没有找到一个合适的解决办法!OpenRead(…)在内部使用GetResponse()方法,因此此方法不起作用。它会下载整个东西。是的,我也试过了。似乎找不到任何允许处理部分web响应的内置类。至少在使用异步操作时是可能的。调用request.Abort,足够早将导致ACK返回并设置“FIN”标志,这将在客户端不接收大量数据的情况下优雅地关闭连接。我唯一的一个小小的疑问是关于设置客户端接收窗口大小的能力…这里有一些关键的更正。。。HEAD可能返回404,但get可能返回200。GET range查询在功能中止后实际上没有什么区别。(应该是,即返回的状态代码小于400)“例如,您可以编写request.AddRange(512)
,只下载文件的前512个字节。”这不应该是-512
?MSDN声明:“如果范围为负,则范围参数指定范围的结束点。服务器应开始将数据从HTTP实体中的数据开始发送到指定的范围参数。”()参见Jim的回答,.Abort方法确实有效,它使用ACK设置FIN标志,从而正常关闭连接