[英]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.Timeout
和HttpWebRequest.ReadWriteTimeout
。 我們需要同時解決這兩個問題 。
HttpWebRequest.ReadWriteTimeout
首先,讓我們處理HttpWebRequest.ReadWriteTimeout
。 我們需要禁用“寫流緩沖”。
httpRequest.AllowWriteStreamBuffering = false;
更改此設置后,您的HttpWebRequest.ReadWriteTimeout
值將如您所願般神奇地工作,您可以將它們保留為默認值(5分鍾),甚至減小它們。 我用60秒。
出現此問題的原因是,當上載大文件時,如果數據由.NET框架緩沖,則您的代碼將認為上載未完成時上載了,並過早調用HttpWebRequest.GetResponse()
,這將超時。
HttpWebRequest.Timeout
現在,讓我們處理HttpWebRequest.Timeout
。
我們遇到的第二個問題是因為HttpWebRequest.Timeout
適用於整個上載。 上傳過程實際上包括三個步驟( 這是我的主要參考文章 )
如果我們有一個超時適用於整個上載,則需要大量來容納較大文件的上載,但是我們也面臨一個問題,即合法的超時將花費很長時間才能真正超時。 這不是一個好情況。 相反,我們希望在短時間內(例如30秒)將時間應用於步驟1和步驟3。 我們根本不希望#2整體超時,但是我們確實希望如果字節停止寫入一段時間后上傳失敗。 幸運的是,我們已經使用HttpWebRequest.ReadWriteTimeout
解決了#2的問題,我們只需要解決HttpWebRequest.Timeout
的令人討厭的行為。 事實證明, GetRequestStream
和GetResponse
的異步版本完全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);
});
}
}
而不是調用GetRequestStream
和GetResponse
我們將調用異步版本:
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.