📌處理 ASP.NET Core Kestrel 綁定失敗(SocketException 10013)與 SYSTEM 佔用 8080 的完整排查與解法

 

前言

最近在部署 ASP.NET Core 應用時遇到一個常見但容易讓人困惑的錯誤:

這代表 Kestrel 嘗試「綁定」某個 network port(例如 80、443、8080)失敗,導致整個服務無法啟動。本文從「錯誤現象」「深入排查」「底層原因」「解法選項」三大面向,用能讓非工程背景的人也理解的語言拆解整個過程,並提供日後內部部屬/操作手冊可直接引用的步驟。



一、錯誤現象(簡單說明給非技術主管)

當我們啟動 ASP.NET Core 應用時,原本應該可以正常對外提供服務,但啟動 log 卻出現:

SocketException (10013): 嘗試存取通訊端被拒絕,因為存取權限不足。

這個錯誤的直觀理解是:應用要聽某個網路埠(port),但系統不讓它綁上去,因此整個服務啟動失敗。這種情況常見原因包括端口已被占用、權限不夠、或系統底層已預先佔著。



二、深入排查步驟(工程師做了什麼、為什麼做)

1. 檢查那個 port 究竟有沒有被佔用

在系統管理員模式下打開 PowerShell,執行:

  netstat -ano | findstr :8080

結果顯示:

TCP    0.0.0.0:8080   LISTENING   PID 4
[::]:8080           LISTENING   PID 4
  • 代表有程式正在監聽 8080 port,而且 PID 是 4

  • PID 4 在 Windows 中是 System(核心系統程序),不是我們自己啟動的應用。

再查這個 PID 是什麼:

tasklist /FI "PID eq 4"

輸出會是:

映像名稱    PID  工作階段名稱   ...
System       4  Services

結論:port 8080 被 Windows 核心(System)透過底層機制佔用,而非我們的 Kestrel 直接衝突。這不是可以 kill 掉的第三方程序(不能強制關閉 PID 4)

2. 進一步查誰在底層控制這個綁定(http.sys / URL reservation)

因為是 SYSTEM 在監聽,通常是 Windows 的底層 HTTP 處理器 http.sys 透過 URL reservation(URLACL) 或某些代理/反向 proxy 註冊了該監聽端。要查清楚,用這兩個指令:

netsh http show urlacl
netsh http show servicestate
  • show urlacl:列出所有已註冊的 URL 綁定(誰有權讓系統監聽某些 URL,例如 http://+:8080/)。

  • show servicestate:更詳細顯示 http.sys 目前的 listener 來源、註冊者、佔用狀態,幫助判定是哪一個 service 或設定讓 System 監聽 8080。

這一步會告訴我們:這個 8080 是被誰「預留」的、是否可以安全移除、或者乾脆不要碰它改路徑。

 


三、底層原因總結(用說故事方式讓非工程理解)

  1. Kestrel 想要綁定某個 port(例如預設 5000/8080),來接受外部請求。

  2. 系統說:「這個 port 已經有人(System)在監聽了」。不是惡意程式搶,而是 Windows 在底層因為某個預先註冊的 URL 設定(例如之前部署過的反向 proxy、IST 或系統 URLACL)把 8080 註冊給了 http.sys。

  3. 因為是 SYSTEM 佔用,不能 kill,要用更溫和的方式:查是哪個設定、改用別的 port、或透過 proxy 做轉發。

  4. 最終應用 log 顯示成功 bind(例如 Now listening on: http://0.0.0.0:8080)表示你後來採用了可行的調整(可能改成另一個內部 port 再做轉發、或原本的佔用與應用共存被允許的路徑被整理好)。



    四、解法選項(實務可行的三條路,附優缺點與推薦)

    方案 A:接受目前狀態,只要服務能正常對外服務就不動(保守)

    • 情境:Kestrel 順利啟動、外部可以正常連線(即使 System 佔 8080),不做改動。

    • 優點:最簡,不動系統底層。

    • 風險:未來變動時可能混淆流量來源,若要明確控制要知道這 8080 是誰在 proxy。

    方案 B(推薦):改讓 Kestrel 綁非衝突內部 port(如 5000),由反向 proxy 或前端入口負責外部 8080 轉發

    • 作法:

      • 修改應用讓 Kestrel 綁定例如 5000options.ListenAnyIP(5000);

      • 透過前端的反向 proxy(IIS、Nginx、公司 edge / Load Balancer)把 http://外部:8080 轉給內部 http://localhost:5000

    • 優點:清楚分層、避開與 http.sys 直接衝突、符合企業部署慣例。

    • 缺點:需要額外一層 proxy 配置。

    方案 C:清理或修改原本預留的 URLACL(非常小心)讓 Kestrel 直接綁 8080

    • 查出是哪個 reservation 在占用(用 netsh http show urlacl / netsh http show servicestate)。

    • 若確認那 reservation 不再需要,可以刪除,例如:

      netsh http delete urlacl url=http://+:8080/
      
    • 然後讓 Kestrel 直接 bind 8080。

    • 注意:這必須確認不是系統依賴,否則會破壞其他服務。

    • 建議在變更前先備份現況、記錄原始 urlacl 表。



      五、實際指令清單(可以直接貼在內部 SOP)

      # 查哪個 process 在監聽 8080(確認是 System)
      netstat -ano | findstr :8080
      tasklist /FI "PID eq 4"
      
      # 查 URL reservation
      netsh http show urlacl
      
      # 查 http.sys listener 詳細來源
      netsh http show servicestate
      
      # 如果確認可以安全釋出 8080 的 reservation(非常小心)
      netsh http delete urlacl url=http://+:8080/
      
      

      註:請用系統管理員權限執行以上指令。

       


      六、建議寫入部署文件用語(對非工程的閱讀者)

      服務啟動失敗檢查清單(Port 綁定錯誤 10013)

      1. 檢查目標 port 是否已被系統佔用(netstat -ano | findstr :<port>)。

      2. 確認 PID 是什麼(tasklist /FI "PID eq <編號>")。

      3. 如果是 System(PID 4)佔用,執行 netsh http show urlaclnetsh http show servicestate 了解來源。

      4. 若不想動系統底層,改用內部 port 並用反向 proxy 做轉發。

      5. 變更前先備份現有 reservation,變更後記錄誰做的、時間與目的。

       


      七、補充(常見問與答)

      Q:為什麼不能 taskkill /PID 4 /F
      A:PID 4 是 Windows 核心系統 System 進程,代表 http.sys 或系統內部機制在監聽,不是用戶層應用,可以說是被系統「保護」,不能強制關閉,這樣做會破壞 Windows 網路子系統。

      Q:我要讓外部用 HTTPS,該怎麼處理?
      A:建議不要讓 Kestrel 直接處理端口 443 的 TLS,改成前端 proxy(例如 IIS/NGINX)做 TLS Termination,再反向 proxy 到內部 HTTP port。這樣憑證管理集中、安全性也更好。



      結語

      這件事情看起來像是「Port 被佔用」,但實際根源是系統層透過 http.sys 做的預留與監聽。透過清楚的排查(netstat → tasklist → netsh)可以釐清責任界線。最穩健的部署方式是讓應用不要和系統底層直接搶 port,而用內部 port + 上層 proxy 分層設計。你可以把本文內容直接抄進內部維運手冊、給 PM/總經理的解釋段落(已用非專業友善語),並標注「已驗證方案 B 為目前部署策略」或後續要改成反向 proxy 的計畫。


      留言

      這個網誌中的熱門文章

      🛠【ASP.NET Core + Oracle】解決 ORA-00904 "FALSE": 無效的 ID 錯誤與資料欄位動態插入顯示問題

      🛠【實戰排除教學】從 VS Code 的 _logger 錯誤,到 PowerShell 找不到 npm/serve,再到 Oracle ORA-03135 連線中斷——一次搞懂!

      🔎如何在 Oracle PL/SQL 儲存過程中為文字欄位加入換行符號(CHR(10))——以 Updlcmremark 為例