6098908eee
- Add Vue components for interactive demos (SSH auth, regex, env vars, ports) - Complete markdown content for SSH, regex, environment variables, and ports - Remove placeholder "待实现" sections and replace with detailed guides - Add visual explanations for key concepts like ports and localhost - Include practical examples and troubleshooting tips - Add component for showing evolution from transistors to CPU - Improve documentation structure and navigation - Add security best practices for API keys and environment variables
249 lines
9.9 KiB
Markdown
249 lines
9.9 KiB
Markdown
# 端口与 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. 什么是端口?(IP 是大楼,端口是房间号)
|
||
|
||
### 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 或代理来解决
|
||
|
||
记住这四句话,你在开发环境里遇到的大多数网络问题,都能快速定位原因。
|