# 連接埠與 localhost 原理

> 💡 **學習指南**：當你執行 `npm run dev`，終端機裡出現 `http://localhost:5173` 時，你有沒有想過：`localhost` 是什麼？`5173` 又代表什麼？為什麼有時候會報 `EADDRINUSE` 錯誤？本章就來把這些日常開發中天天見、卻很少深究的概念一次講透。

在開始之前，建議你先補兩塊「基礎磚」：

- **網路基礎**：如果你不太清楚 IP 位址和 HTTP 的概念，可以先看 [電腦基礎 - 網路通訊](../1-computer-fundamentals/network-fundamentals.md) 部分。
- **終端機基礎**：如果你還不熟悉終端機命令列，可以先看 [命令列與 Shell 腳本導論](./command-line-shell.md)。

---

## 0. 引言：那個天天見的 `localhost:5173` 到底概述

<DevServerFlowDemo />

每個開發者的日常都離不開這一行輸出：

```
➜  Local:   http://localhost:5173/
```

但你有沒有想過，這短短一行字裡，藏著好幾個關鍵概念：

- **http://** → 通訊協定（用什麼語言對話）
- **localhost** → 目標位址（找誰）
- **:5173** → 連接埠號（找到之後，敲哪扇門）

搞懂這三件事，你就能理解 90% 的開發環境網路問題。接下來我們逐個拆解。

---

## 1. 連接埠 概述

### 1.1 一個直覺比喻

想像一台伺服器是一棟大樓：

- **IP 位址**（如 `192.168.1.100`）就是大樓的門牌位址——告訴你「去哪棟樓」。
- **連接埠號**（如 `:80`）就是樓裡的房間號——告訴你「進哪間房」。

一棟樓裡可以同時有餐廳（80 號房）、咖啡廳（443 號房）、辦公室（22 號房）。同理，一台電腦上可以同時執行 Web 伺服器、資料庫、SSH 服務，各自佔用不同的連接埠。

👇 **動手試試看**：
點擊下面的「房間門牌」，模擬向不同連接埠發起連線。注意觀察：當連接埠「開著」（有程式在監聽）和「關著」時，分別會發生什麼？

<PortAnalogyDemo />

### 1.2 連接埠號的取值範圍

連接埠號是一個 **0–65535** 之間的整數（共 65536 個）。這麼多連接埠被分為三個區間：

| 區間 | 範圍 | 用途 | 舉例 |
| :--- | :--- | :--- | :--- |
| **系統連接埠** | 0 – 1023 | 預留給標準協定，一般使用者不能隨意佔用 | 80 (HTTP)、443 (HTTPS)、22 (SSH) |
| **註冊連接埠** | 1024 – 49151 | 給常見應用註冊使用 | 3306 (MySQL)、5432 (PostgreSQL)、6379 (Redis) |
| **動態連接埠** | 49152 – 65535 | 作業系統臨時分配 | 瀏覽器發請求時，系統隨機分配一個來源連接埠 |

> 為什麼你的開發伺服器喜歡用 3000、5173、8080？因為這些都在「註冊連接埠」範圍內，不需要管理員權限就能監聽，又不太容易和系統服務衝突。

### 1.3 開發中常見的連接埠號速查

👇 **動手試試看**：
輸入連接埠號或服務名搜尋，點擊任意一行可以展開查看使用範例。

<CommonPortsDemo />

---

## 2. localhost 概述

### 2.1 「環回」的核心概念

`localhost` 是一個特殊的網域名稱，它永遠指向**你自己的這台電腦**。

當你在瀏覽器輸入 `http://localhost:3000` 時，發生了這些事：

1. 瀏覽器問作業系統：「`localhost` 的 IP 是多少？」
2. 作業系統直接回答：「`127.0.0.1`」（不需要聯網查 DNS）
3. 資料封包發往 `127.0.0.1`，但**不會真的離開本機**
4. 作業系統透過「環回介面（loopback interface）」把資料封包**折返**回來
5. 監聽在 3000 連接埠上的程式收到請求，返回回應

**整個過程不經過網路線、不經過路由器、不需要聯網。**

👇 **動手試試看**：
點擊「發送請求」，觀察資料封包的完整旅程。然後點擊下方的「馬甲卡片」，了解 localhost 的幾種寫法和區別。

<LocalhostLoopbackDemo />

### 2.2 `localhost` vs `127.0.0.1` vs `0.0.0.0`

這三個概念經常被混淆，但它們的含意完全不同：

| 寫法 | 含意 | 誰能存取 |
| :--- | :--- | :--- |
| `localhost` / `127.0.0.1` | 環回位址，僅本機 | 只有你自己的電腦 |
| `0.0.0.0` | 監聽所有網路介面 | 本機 + 區域網路內其他裝置 |
| `192.168.x.x` | 區域網路 IP | 區域網路內的裝置 |

**實際場景**：

```bash
# 只有自己能存取（安全，適合開發）
npm run dev -- --host localhost

# 手機也能存取（適合行動端除錯）
npm run dev -- --host 0.0.0.0
```

> 很多框架（如 Vite、Next.js）預設監聽 `localhost`，所以你的手機即使連著同一個 WiFi 也存取不了。想用手機除錯？加上 `--host` 參數就行。

---

## 3. 連接埠衝突：最常見的開發環境問題

### 3.1 會衝突的動機

**一個連接埠同一時刻只能被一個程式監聽。** 這就像一個房間只能住一戶人家。

如果你嘗試在同一個連接埠上啟動第二個服務，就會看到這個經典錯誤：

```
Error: listen EADDRINUSE :::3000
```

翻譯成人話就是：**「3000 號房已經有人住了，你進不去！」**

常見的衝突場景：
- 上次的開發伺服器沒關乾淨，還在背景執行
- 兩個不同的專案用了相同的預設連接埠
- 某個系統服務已經佔用了你想要的連接埠

👇 **動手試試看**：
試著在下面的模擬器裡多次啟動服務。當連接埠衝突時，對比「直接啟動」和「智慧啟動」的不同處理方式。

<PortConflictDemo />

### 3.2 排查與解決

遇到連接埠衝突時，排查流程非常固定：

**macOS / Linux：**
```bash
# 第一步：查看誰在佔用 3000 連接埠
lsof -i :3000

# 第二步：拿到 PID 後，強制終止
kill -9 <PID>
```

**Windows：**
```bash
# 第一步：查看誰在佔用 3000 連接埠
netstat -ano | findstr :3000

# 第二步：終止行程
taskkill /PID <PID> /F
```

> 很多現代框架（Vite、Create React App 等）遇到連接埠衝突時會自動詢問「是否換一個連接埠？」。但了解底層原理，能幫你更快地排查那些框架幫不了你的疑難雜症。

---

## 4. 開發中的「同源策略」與跨域

### 4.1 源 概述

瀏覽器有一個安全機制叫做**同源策略（Same-Origin Policy）**：只有**協定、域名、連接埠**三者完全一致，才算「同源」。

| 位址 A | 位址 B | 是否同源 | 原因 |
| :--- | :--- | :--- | :--- |
| `http://localhost:5173` | `http://localhost:5173/about` | ✅ 同源 | 協定、域名、連接埠都一樣 |
| `http://localhost:5173` | `http://localhost:3000` | ❌ 不同源 | **連接埠不同**（5173 vs 3000） |
| `http://localhost:5173` | `https://localhost:5173` | ❌ 不同源 | **協定不同**（http vs https） |

### 4.2 前後端分離必然遇到跨域的動機

當你的專案架構是：

```
前端 (Vite)  →  http://localhost:5173
後端 (Express) →  http://localhost:3000
```

前端頁面從 `:5173` 載入，然後用 `fetch('/api/users')` 去請求 `:3000` 的介面——**連接埠不一樣，觸發跨域限制！**

**兩種常見解決方案：**

**方案一：後端設定 CORS**
```javascript
// Express 後端
app.use(cors({ origin: 'http://localhost:5173' }))
```

**方案二：前端設定代理（推薦）**
```javascript
// vite.config.js
export default {
  server: {
    proxy: {
      '/api': 'http://localhost:3000'
    }
  }
}
```

代理的原理：讓 Vite 開發伺服器幫你「轉發」請求。瀏覽器以為自己在和 `:5173` 通訊（同源），實際上 Vite 在背後偷偷幫你把請求轉給了 `:3000`。

---

## 5. 實戰排查：三個最常見的問題

👇 **動手試試看**：
選擇一個你遇到的問題，跟著步驟一起排查。每一步都可以點擊「執行」查看輸出。

<PortTroubleshootDemo />

---

## 6. 名詞對照表

| 英文術語 | 中文對照 | 解釋 |
| :--- | :--- | :--- |
| **Port** | 連接埠 | 一個 0–65535 的數字，用來區分同一台機器上的不同網路服務。每個服務「監聽」一個連接埠，等待客戶端連線。 |
| **localhost** | 本機主機 | 一個特殊網域名稱，永遠指向本機（127.0.0.1）。用於在不聯網的情況下存取本機上執行的服務。 |
| **Loopback Interface** | 環回介面 | 作業系統的虛擬網路介面。發往 127.0.0.1 的資料封包不會離開本機，而是透過該介面「折返」回來。 |
| **EADDRINUSE** | 位址已被使用 | Node.js / 作業系統報的錯誤，表示你要監聽的連接埠已經被另一個程式佔用了。 |
| **CORS** | 跨域資源共享 | 瀏覽器安全機制。當前端頁面嘗試請求不同源（協定/域名/連接埠不同）的介面時，需要後端明確許可。 |
| **Same-Origin Policy** | 同源策略 | 瀏覽器的安全基石：只允許同協定、同域名、同連接埠的請求自由通訊，阻止跨域的資料讀取。 |
| **Proxy** | 代理 | 在開發環境中，代理伺服器代替瀏覽器向後端轉發請求，繞過瀏覽器的同源限制。 |
| **0.0.0.0** | 所有介面 | 當服務監聽 0.0.0.0 時，表示它接受來自任何網路介面（本機、區域網路等）的連線。 |
| **Well-known Ports** | 知名連接埠 | 0–1023 連接埠的統稱，預留給 HTTP (80)、HTTPS (443)、SSH (22) 等標準協定。 |
| **PID** | 行程 ID | 作業系統為每個執行中的程式分配的唯一編號，用於管理和終止行程。 |
| **lsof** | 列出開啟的檔案 | macOS/Linux 命令，用於查看哪個行程佔用了某個連接埠（`lsof -i :連接埠號`）。 |
| **HMR** | 熱模組替換 | 開發伺服器的功能：你修改程式碼後，瀏覽器自動更新，無需手動重新整理頁面。底層透過 WebSocket 通知瀏覽器。 |

---

## 總結

連接埠和 localhost 是開發環境中最基礎、最高頻的概念：

- **連接埠** = 一台機器上區分不同服務的「門牌號」（0–65535）
- **localhost** = 「自己找自己」的特殊位址（127.0.0.1），資料不出本機
- **連接埠衝突**的本質是「一個門牌只能掛一塊牌子」
- **跨域**的本質是「連接埠不同 = 不同源」，需要 CORS 或代理來解決

記住這四句話，你在開發環境裡遇到的大多數網路問題，都能快速定位原因。
