小明回報系統錯誤紀錄噴出以下例外錯誤
Unable to connect to the remote server
InnerException
Only one usage of each socket address (protocol/network address/port) is normally permitted
重新檢視小明的原始碼
public string GetContent()
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://blog.flash.com/");
httpClient.DefaultRequestHeaders.ConnectionClose = true;
var result = httpclient.GetAsync("").GetAwaiter().GetResult();
return result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
}
發現HttpClient 有實作IDisposable 介面, 依據.NET 開發人員多年所受訓練的反射動作: 遇到IDiposable 就該用using 包覆使用範圍, 用畢要儘早釋放.
public string GetContent()
{
using(var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri("http://blog.flash.com/");
httpClient.DefaultRequestHeaders.ConnectionClose = true;
var result = httpclient.GetAsync("").GetAwaiter().GetResult();
return result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
}
}
結果仍然還是有同樣的問題, 查了原因是由於HttpClient 底層沒有實作Connetion Pool 無法重複使用連線, 程式將對同一個台網站開啟 100 條Socket 連線, 而這100 條連線在HttpClient Dispose() 後會以TIME_WAIT 狀態繼續存活240 秒才被真的釋放. 當殘留連線數量龐大時, 用光可用Socket Port 號碼, 導致無法再建立新連線.
對於HttpClient 的最佳使用方式, 一派主張因每個HttpClient 都會建立新連線, 為免耗盡Socket 資源應儘量共用. 另一派則認為長期使用現在連線, 會遇到DNS 異動將無法更新. 故設計人員將HttpClient 何時建立及消滅設成選項, 讓開發者自己選.
發現ASP.NET Core 2.1 / .NET 4.6 起推出HttpClientFactory 一舉改善前述問題.
.NET Core 已預設參考Microsoft.AspNetCore.App ,已內建Microsoft.Extensions.Http plugin ,故無須額外安裝套件
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
}
在需要使用 HttpClient 的 class 中透過 .NET Core 的建構式注入
private readonly IHttpClientFactory _clientFactory;
public HomeController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
需要使用HttpClient 直接透過CreateClient 方法取得即可
using(var client = _clientFactory.CreateClient())
{
...
}
或者你也能在註冊時候指定名稱及預做基本設定
services.AddHttpClient("flashblog", c =>
{
c.BaseAddress = new Uri("https://blog.flash.com/");
});
在需要的時候直接透過CreateClient 方法並指定名稱即可
var client = _clientFactory.CreateClient("flashblog");
var client = HttpClientFactory.CreateClient();
...
WebClient 方法沒被設計成Thread-Safe (多緒執行時會丟 NotSupportedException), 使用時用完即拋策略就對了, 每次建立WebClient 都會開新連線, 但它已是昨日黃花, 寫新程式就別再用WebClient , 改用 HttpClient 吧!(除非是 .NET 3.5/4.0)
Windows 可使用的port 設定可以查詢
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\MaxUserPort
Windows 10 預設值為15000
Windows 環境可以透過下面設定縮短TIME-WAIT 時間
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay