WebRequest.txt 適用版本: .NET 4.5 以下, 建議使用 WebClient. .NET 4.5 以上(含), 建議使用 HttpClient. .NET 4.6.1 以上, 建議使用 HttpClientFactory. (需另外安裝 Microsoft.Extensions.Http NuGet 套件) 參考: HttpWebRequest vs WebClient vs HttpClient.txt ---------- 20190711 https://blog.yowko.com/webrequest-and-httpwebrequest/ https://blog.yowko.com/webclient/ https://blog.yowko.com/httpclient/ https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=netframework-4.7.2 HttpClient: 1. .NET 4.5以上支援. 2. 支援 DNS 解析快取、Cookie / 身分驗證設定,可同時發送多個 Request、基於 HttpWebRequest/HttpWebResponse(易於測試)、 IO 相關方法均採非同步等優點,缺點是不支援 FTP 3. 使用時, 不建議呼叫完畢立刻 dispose 釋放資源, 而應採用共用的方式. 原因是底層沒有實作(Connection Pool)的機制, 若重複socket連線時, 則每個socket連線在 dispose 之後, 會以 TIME_WAIT 狀態繼續存活 240 秒才被真的釋放. 當殘留連線數量龐大時,除了消耗記憶體資源,甚至可能用光可用 Socket Port 號碼,導致無法再建立新連線, 因此建議應該要重複使用連線. 4. 重複使用時的衍生問題: 參考如下: https://blog.yowko.com/httpclient-not-respect-dns-change/ 共用靜態 HttpClient 可共用連線避免 TIME_WAIT 連線殘留,但這也衍生新問題 - 當 HttpClient 使用 xxx.yyy.zzz DNS 名稱連上網站,它會記憶 DNS 解析結果,但因缺乏失效機制快取將永久有效,若 DNS 記錄修改,必須重新啟動程序才會重新解析 DNS 取得新 IP。 在一些實務情境,程式可沒法說重啟就重啟,針對此有個簡單解法是對特定網站指定 ConnectionLeaseTimeout,強迫 .NET 在一段時間後關閉連線,下次重建連線將可重新解析 DNS。 var sp = ServicePointManager.FindServicePoint(new Uri("http://xxx.yyy.zzz")); sp.ConnectionLeaseTimeout = 600*1000; // 10分鐘 實務應用上,若 HttpClient 用於 Web API 客戶端,Web API URL 是固定的,可在 HttpClient 建構時一併指定 ConnectionLeaseTimeout;若為動態傳入 URL 參數,則需每次存取網站前針對該 URL 設定。除此之外,還有 Dispose HttpClient、指定 HttpClient.DefaultRequestHeaders.ConnectionClose = true 等做法,也可克服 HttpClient 不反映 DNS 異動問題。延伸閱讀:HttpClient 無法反應 DNS 異動的解決方式 5. 若為(ASP.NET Core 2.1 / .NET 4.6)以上, 則改用 HttpClientFactory. 藉由 Connection Pool 機制一舉改善重複使用問題。詳情可參考 Yowko 的文章:在 .NET Core 與 .NET Framework 上使用 HttpClientFactory ref: https://blog.yowko.com/httpclientfactory-dotnet-core-dotnet-framework/ NET Core 因已預設參考 Microsoft.AspNetCore.App 的 metapackage,已內建 Microsoft.Extensions.Http plugin,故無須額外安裝套件 .NET Framework 需另外安裝 Microsoft.Extensions.Http NuGet 套件 WebClient: 1. .NET 4.5以上建議不再使用, 建議改用 System.Net.Http.HttpClient 2. 使用時採隨用隨建,用完即拋策略就對了,每次建立 WebClient 都會開新連線 ---------- 20190711 The following example shows how to create a WebRequest instance and return the response. using System; using System.IO; using System.Net; using System.Text; namespace Examples.System.Net { public class WebRequestGetExample { public static void Main () { // Create a request for the URL. WebRequest request = WebRequest.Create ("http://www.contoso.com/default.html"); // If required by the server, set the credentials. request.Credentials = CredentialCache.DefaultCredentials; // Get the response. HttpWebResponse response = (HttpWebResponse)request.GetResponse (); // Display the status. Console.WriteLine (response.StatusDescription); // Get the stream containing content returned by the server. Stream dataStream = response.GetResponseStream (); // Open the stream using a StreamReader for easy access. StreamReader reader = new StreamReader (dataStream); // Read the content. string responseFromServer = reader.ReadToEnd (); // Display the content. Console.WriteLine (responseFromServer); // Cleanup the streams and the response. reader.Close (); dataStream.Close (); response.Close (); } } }