簡體   English   中英

如何在C#中為大型HTTP請求設置HttpWebRequest.Timeout

[英]How to set HttpWebRequest.Timeout for a large HTTP request in C#

我不知道如何處理HttpWebRequest.Timeout。 以前,我曾經為簡單明了的Socket對象設置超時:超時設置了發送或接收數據塊的最大時間。 但是,似乎HttpWebRequest.Timeout設置了整個HTTP請求的超時。 如果請求很大(例如,我正在使用HTTP PUT上傳一個大文件),則可能需要幾個小時。 這導致我設置:

...
request.Timeout = System.Threading.Timeout.Infinite;
Stream requestStream = request.GetRequestStream();
for (something)
{
  ...
  requestStream.Write(b, 0, b.Length);
}

但是,這是否不意味着如果與服務器的網絡連接被卡住,我將得到requestStream.Write永不拋出“操作超時”異常? 這樣超時的概念在這種情況下不起作用?

理想情況下,我希望.Timeout設置僅影響單個requestStream.Write。 我可以根據需要設置多個Write(),只要每個都不超過.Timeout值即可。

還是我需要實現自己的基於套接字的機制來實現這一目標?

另外,設置斷點時,我發現requestStream實際上是具有.Timeout = 300000(300秒)的ConnectStream實例。 這不是說Infinite實際上不是那個Infinite,而是限制為300秒嗎? 對於大文件和慢速連接,這是一個相當困難的限制。

在處理大文件上傳時有兩個超時困擾我們。 HttpWebRequest.TimeoutHttpWebRequest.ReadWriteTimeout 我們需要同時解決這兩個問題

HttpWebRequest.ReadWriteTimeout

首先,讓我們處理HttpWebRequest.ReadWriteTimeout 我們需要禁用“寫流緩沖”。

httpRequest.AllowWriteStreamBuffering = false;

更改此設置后,您的HttpWebRequest.ReadWriteTimeout值將如您所願般神奇地工作,您可以將它們保留為默認值(5分鍾),甚至減小它們。 我用60秒。

出現此問題的原因是,當上載大文件時,如果數據由.NET框架緩沖,則您的代碼將認為上載未完成時上載了,並過早調用HttpWebRequest.GetResponse() ,這將超時。

HttpWebRequest.Timeout

現在,讓我們處理HttpWebRequest.Timeout

我們遇到的第二個問題是因為HttpWebRequest.Timeout適用於整個上載。 上傳過程實際上包括三個步驟( 這是我的主要參考文章

  1. 開始上傳的請求。
  2. 字節的寫入。
  3. 完成上傳並從服務器獲得任何響應的請求。

如果我們有一個超時適用於整個上載,則需要大量來容納較大文件的上載,但是我們也面臨一個問題,即合法的超時將花費很長時間才能真正超時。 這不是一個好情況。 相反,我們希望在短時間內(例如30秒)將時間應用於步驟1和步驟3。 我們根本不希望#2整體超時,但是我們確實希望如果字節停止寫入一段時間后上傳失敗。 幸運的是,我們已經使用HttpWebRequest.ReadWriteTimeout解決了#2的問題,我們只需要解決HttpWebRequest.Timeout的令人討厭的行為。 事實證明, GetRequestStreamGetResponse的異步版本完全GetResponse我們的需求。

因此,您需要以下代碼:

public static class AsyncExtensions
{
    public static Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeout)
    {
        return Task.Factory.StartNew(() =>
        {
            var b = task.Wait((int)timeout.TotalMilliseconds);
            if (b) return task.Result;
            throw new WebException("The operation has timed out", WebExceptionStatus.Timeout);
        });
    }
}

而不是調用GetRequestStreamGetResponse我們將調用異步版本:

var uploadStream = httpRequest.GetRequestStreamAsync().WithTimeout(TimeSpan.FromSeconds(30)).Result;

同樣,響應:

var response = (HttpWebResponse)httpRequest.GetResponseAsync().WithTimeout(TimeSpan.FromSeconds(30)).Result;

這就是您所需要的。 現在您的上傳將更加可靠。 您可以將整個上傳文件包裝在重試循環中以增加確定性。

請注意,對於具有mono的 HttpWebRequest,這是非常重要的(如Boinst所述)

httpRequest.AllowWriteStreamBuffering = false;

我花了整整一天的時間在Wireshark追查為什么大文件上傳失敗的原因,最后偶然發現了這個快速,簡單的答案。

最簡單的方法是創建自己的繼承WebRequest的類

    public class MyRequest : WebRequest
    {
        public MyRequest()
        {
            this.Timeout = 1234;
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM