12 年前工業級設備環境還有 Win2k WorkStation ,當時開發的回傳走 http 是用 .Net framework 2.0 ,2.0 是能安裝在 Windows 2000 或 Windows Me 以前 OS 的最後一個版本,那個年代雖然已經有 https 了,但是用的不多,所以當時回傳資料的程式開發時,就直接這樣用,再於回傳裡面標記檢查資訊。
這周有個客戶希望把資料也扔他們那一份,當初是用節區架構建的,所以在參數檔新增他們的節區即可,這部分是同事處理,連續兩天溝通確認扔不過去,我介入處理後,先調出 log 檔的錯誤訊息。
大概常在網路上讀文章,一看錯誤訊息就推論疑似是 https 問題,檢查網址果然是 https 。
馬上 google 搜尋 WebClient https ,找到黑大的文:
原則上有兩個解決方向:
- 改程式碼,加入 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
- 改機碼,設定 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft.NETFramework\v4.0.30319\SchUseStrongCrypto=dword:00000001
改程式碼部分,搜尋 MSDN 查屬性,在 .Net framework 4.0 前只能設定 Ssl3 跟 Tls ,但就是這兩個加密協定被停用。參考下面連結 (從左邊下拉式可改 .Net 版本)
所以改試機碼變更,在 .Net framework 2.0 不吃 SchUseStrongCrypto 。
於是再祭出 google 大神搜尋 WebClient https “.Net framework 2.0″ site:support.microsoft.com ,得到:
TLS 系統預設版本包含在.NET Framework 2.0 SP2,Windows Vista SP2 和 Server 2008 SP2 的支援
這邊是說 .Net framework 2.0 SP2 已經加入相關加密協定支援,機碼用:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft.NETFramework\v2.0.50727\SystemDefaultTlsVersions"=dword:00000001
即可。
我是跑 32 bits Application On Windows 10 IoT x64 ,所以要改用機碼 (Windows 64 On Windows 32):
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft.NETFramework\v2.0.50727\SystemDefaultTlsVersions"=dword:00000001
一測試就可以通聯,問題解決關案,還不用改程式碼。
另外這個 KB3154517 有寫到,在 SP2 列舉常數 SecurityProtocolType 已經加入 Tls11 / Tls12 的支援,所以也是可以改原始碼的。
不過事情解決了,就懶得改原始碼,把機碼匯出來,直接變成選項載入。
最後,.Net framework 相關文件都建議停用 WebClient ,改用 HttpClient ,作為本篇附註參考。
錯誤訊息參考:
[2022/03/日期 時:分:02.104] 基礎連接已關閉: 傳送時發生未預期的錯誤。
System
於 System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request)
於 System.Net.WebClient.DownloadData(Uri address)
於 System.Net.WebClient.DownloadData(String address)
於 [AppName].[模組].[方法](String sUrl, Object nowQueryEvent)
於 [AppName].[視窗].[事件]()System.Net.WebException: 基礎連接已關閉: 傳送時發生未預期的錯誤。 ---> System.IO.IOException: 收到來自傳輸資料流的未預期 EOF 或 0 個位元組。
於 System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count)
於 System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
於 System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
於 System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
於 System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
於 System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
於 System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
於 System.Net.TlsStream.CallProcessAuthentication(Object state)
於 System.Threading.ExecutionContext.runTryCode(Object userData)
於 System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
於 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
於 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
於 System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)
於 System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)
於 System.Net.PooledStream.Write(Byte[] buffer, Int32 offset, Int32 size)
於 System.Net.ConnectStream.WriteHeaders(Boolean async)
--- 內部例外狀況堆疊追蹤的結尾 ---
於 System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request)
於 System.Net.WebClient.DownloadData(Uri address)
於 System.Net.WebClient.DownloadData(String address)
於 [AppName].[模組].[方法](String sUrl, Object nowQueryEvent)
於 [AppName].[視窗].[事件]()
引用通告: [VBA] 抓取 Json 檔案轉入 Excel | 鄭子璉