前言:
前段時間在對接第三方接口的時候發生了一個非常奇葩的問題,就是使用 .NET Framework 4.6 HttpWebRequest進行網絡請求的相關問題。背景,關於調用第三方的接口都是使用使用自己封裝的一個HttpWebRequestHepler幫助類,在本地開發時調用第三方接口都是正常的。然而當我部署到運維給我一個服務器(阿里雲服務器)時剛開始提示是請求被中止: 未能創建 SSL/TLS 安全通道,之后經過一番修改以后就是提示基礎連接已經關閉: 發送時發生錯誤。之后嘗試了各種方法,還是沒有辦法解決基礎連接已經關閉: 發送時發生錯誤這個問題。最后真的是無能為力,光這個問題找了一下午的解決方案,最后換到了我自己的阿里雲服務器是可以正常調通第三方接口的。然后讓運維看了下服務器結果是這個服務器都沒有開通外網,所以導致了這個問題的出現。下面記錄下問題排除的過程,希望能夠幫助到遇到這種坑的小伙伴。
一、自己封裝的一個通用的HttpWebRequestHepler Http Web網絡請求幫助類:
/// <summary> /// Http Web網絡請求幫助類 /// </summary> public class HttpWebRequestHepler { private static HttpWebRequestHepler _httpWebRequestHepler; private string _resContent;//響應內容 private string _errInfo;//錯誤信息 private int _responseCode;//響應狀態碼 public static HttpWebRequestHepler _ { get => _httpWebRequestHepler ?? (_httpWebRequestHepler = new HttpWebRequestHepler()); set => _httpWebRequestHepler = value; } /// <summary> /// 數據請求 /// </summary> /// <param name="requestUrl">請求地址</param> /// <param name="postData">請求參數</param> /// <param name="accessToken">授權token</param> /// <param name="contentType">請求標頭值類型</param> /// <param name="method">請求方式</param> /// <returns></returns> public string HttpWebResponseData(string requestUrl, string postData, string accessToken = "", string contentType = "application/json", string method = "POST") { HttpWebResponse wr = null; try { var hp = (HttpWebRequest)WebRequest.Create(requestUrl); hp.Timeout = 60 * 1000 * 10;//以毫秒為單位,設置等待超時10分鍾 hp.Method = method; hp.ContentType = contentType; if (!string.IsNullOrWhiteSpace(accessToken)) { hp.Headers.Add("Authorization", "Bearer " + accessToken);//增加headers請求頭信息 } if (postData != "")//帶參數請求 { byte[] data = Encoding.UTF8.GetBytes(postData); hp.ContentLength = data.Length; Stream ws = hp.GetRequestStream(); // 發送數據 ws.Write(data, 0, data.Length); ws.Close(); } wr = (HttpWebResponse)hp.GetResponse(); var sr = new StreamReader(wr.GetResponseStream() ?? throw new InvalidOperationException(), Encoding.UTF8); this._resContent = sr.ReadToEnd(); sr.Close(); wr.Close(); } catch (Exception exp) { this._errInfo += exp.Message; if (wr != null) { this._responseCode = Convert.ToInt32(wr.StatusCode); } return this._resContent; } this._responseCode = Convert.ToInt32(wr.StatusCode); return this._resContent; } }
二、請求被中止: 未能創建 SSL/TLS 安全通道問題解決:
把項目部署到阿里雲服務器中,請求第三方提示請求被中止: 未能創建 SSL/TLS 安全通道。首先字面上可以看出來這個https請求安全協議的問題。微軟官方說明是,NET 4.6需要添加
ServicePointManager.SecurityProtocol屬性,指定schnanel安全包支持的安全協議。
微軟官方解釋:
此屬性選擇要用於新連接的安全套接字層 (SSL) 或傳輸層安全性 (TLS) 協議的版本;不會更改現有連接。
從 .NET Framework 4.7 開始,此屬性的默認值為 SecurityProtocolType.SystemDefault 。 這允許基於 SslStream ((如 FTP、HTTP 和 SMTP) )的 .NET Framework 網絡 api 從操作系統或系統管理員執行的任何自定義配置繼承默認安全協議。 有關默認情況下在每個版本的 Windows 操作系統上啟用了哪些 SSL/TLS 協議的信息,請參閱 TLS/SSL (SCHANNEL SSP) 中的協議 。
對於通過 .NET Framework 4.6.2 的 .NET Framework 版本,不會列出此屬性的默認值。 安全環境不斷變化,默認的協議和保護級別會隨着時間的推移而更改,以避免已知的漏洞。 默認值因單獨的計算機配置、已安裝的軟件和應用的修補程序而異。
解決方案:
//todo:指定請求包的安全協議,因為不知道你當前項目到底是哪個版本所以為了安全保障都加上 ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.SystemDefault | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
三、基礎連接已經關閉: 發送時發生錯誤
這個問題查閱了網上幾個比較典型的博客試了下,結果都沒有辦法解決我的問題,一下記錄下這幾個博客的解決方案,希望可以幫助到遇到這樣問題的小伙伴。
1、一般來說添加了上面的ServicePointManager.SecurityProtocol屬性就可以解決這個基礎連接關閉的問題。
2、C# HttpRequest基礎連接已經關閉: 接收時發生意外錯誤(原文地址):
//增加下面兩個屬性即可 hp.KeepAlive = false; hp.ProtocolVersion = HttpVersion.Version10;
四、開啟阿里雲服務器外網(我的解決方案)
查看一下你的服務器是否開通了外網,假如沒有開通服務器外網在進行嘗試。阿里雲服務器配置外網訪問參考。因為這個奇葩問題花費了一天寶貴的時間,考慮問題還是得多方面考慮。