Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/298.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在不下载内容的情况下执行GET请求?_C#_.net_Httpwebrequest_Servicepoint - Fatal编程技术网

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
    返回后中止

注意,尽管手工编写HTTP版本相当简单,HTTPS版本还是让我有点害怕。(也许有一个操作系统库已经做到了这一点?)尝试部分下载。可以只下载一个带有range http头的范围。@rekire
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标志,从而正常关闭连接