端口与 localhost
原始素材:
2-开发环境与工具/2.05-ports-localhost.md
这篇解决什么问题
当你执行 npm run dev,终端里出现:
Local: http://localhost:5173/
这短短一行里其实包含三个关键概念:
http://:通信协议,决定浏览器和服务端用什么规则对话。localhost:目标地址,表示访问你自己的电脑。:5173:端口号,表示访问这台电脑上的哪一个服务。
理解端口和 localhost,可以帮你解决很多开发环境里的常见问题:
- 为什么
localhost:5173能打开,手机却访问不了。 - 为什么启动项目时报
EADDRINUSE。 - 为什么前端
5173请求后端3000会跨域。 - 为什么
127.0.0.1、localhost、0.0.0.0不是一回事。 - 为什么数据库端口不能直接暴露到公网。
开发服务器是什么
开发服务器可以理解为一个运行在本机上的 HTTP 服务。它一直监听某个端口,等待浏览器请求。
以 Vite 为例:
浏览器请求 http://localhost:5173/
-> Vite 开发服务器收到请求
-> 即时编译 Vue / React / TypeScript / CSS
-> 返回浏览器可执行的 HTML、JS、CSS
所以开发服务器不是简单的「打开文件」,它通常还负责:
- 本地 HTTP 服务。
- 即时代码编译。
- 模块解析。
- HMR 热更新。
- 静态资源返回。
- API 代理。
什么是端口
可以把一台电脑想象成一栋大楼:
- IP 地址是大楼地址,告诉你去哪台机器。
- 端口号是房间号,告诉你找这台机器上的哪个服务。
一台电脑可以同时运行多个网络服务:
| 服务 | 常见端口 | 说明 |
|---|---|---|
| HTTP | 80 |
普通网页访问 |
| HTTPS | 443 |
加密网页访问 |
| SSH | 22 |
远程登录 |
| MySQL | 3306 |
MySQL 数据库 |
| Redis | 6379 |
Redis 缓存 |
| Vite | 5173 |
前端开发服务器 |
端口的本质是操作系统用来区分网络服务的编号。客户端连到某台机器后,操作系统根据端口号把连接交给对应的进程。
端口范围
端口号是 0-65535 之间的整数,共 65536 个。
| 区间 | 范围 | 用途 | 例子 |
|---|---|---|---|
| 系统端口 | 0-1023 |
预留给标准协议,通常需要管理员权限 | 80 HTTP、443 HTTPS、22 SSH |
| 注册端口 | 1024-49151 |
常见应用使用,开发中最常见 | 3000、5173、8080、3306 |
| 动态端口 | 49152-65535 |
操作系统临时分配 | 浏览器请求外部网站时使用的源端口 |
开发服务器常用 3000、5173、8000、8080,因为它们不需要管理员权限,也不容易和系统标准服务冲突。
常见端口速查
| 端口 | 服务 | 用途 | 暴露风险 |
|---|---|---|---|
21 |
FTP | 文件传输 | 高 |
22 |
SSH | 远程登录 | 中,需要限制来源和密钥 |
25 |
SMTP | 邮件发送 | 中 |
53 |
DNS | 域名解析 | 中 |
80 |
HTTP | 未加密网页访问 | 低到中 |
443 |
HTTPS | 加密网页访问 | 低 |
3000 |
Node / React | 本地开发服务 | 低,生产需谨慎 |
3306 |
MySQL | 数据库 | 高,不应公网暴露 |
4200 |
Angular | 本地开发服务 | 低 |
5000 |
Flask | Python Web 服务 | 低到中 |
5173 |
Vite | 本地开发服务 | 低 |
5432 |
PostgreSQL | 数据库 | 高,不应公网暴露 |
6379 |
Redis | 缓存 | 高,不应公网暴露 |
8000 |
Django / Python | 本地或测试 HTTP 服务 | 低到中 |
8080 |
通用 HTTP | 备用 HTTP、代理、Java 服务 | 低到中 |
27017 |
MongoDB | 数据库 | 高,不应公网暴露 |
安全原则很简单:数据库、缓存、管理后台、SSH 等端口不要直接暴露到公网。生产环境应通过内网、安全组、防火墙、VPN、SSH 隧道或零信任访问控制来限制入口。
什么是 localhost
localhost 是一个特殊域名,表示「本机」。
当你访问:
http://localhost:3000
大致发生这些事:
- 浏览器把
localhost交给操作系统解析。 - 操作系统把它解析为
127.0.0.1,通常不需要请求外部 DNS。 - 数据包发往
127.0.0.1。 - 操作系统通过环回接口(loopback interface)把数据包折回本机。
- 监听在
3000端口的程序收到请求并返回响应。
整个过程不经过路由器、不需要网线,也不需要联网。
localhost、127.0.0.1、::1、0.0.0.0
这几个地址经常被混淆。
| 写法 | 含义 | 谁能访问 | 常见用途 |
|---|---|---|---|
localhost |
本地主机域名,通常解析到 127.0.0.1 或 ::1 |
本机 | 本地开发 |
127.0.0.1 |
IPv4 环回地址 | 本机 | 明确访问 IPv4 本机服务 |
::1 |
IPv6 环回地址 | 本机 | IPv6 环境下的本机访问 |
0.0.0.0 |
监听所有 IPv4 网络接口 | 本机和可达网络内设备 | 允许局域网访问开发服务 |
192.168.x.x |
局域网地址 | 同一局域网设备 | 手机、平板访问电脑服务 |
关键区别:
- 访问
localhost/127.0.0.1:只找自己这台电脑。 - 服务监听
localhost/127.0.0.1:只有本机能访问。 - 服务监听
0.0.0.0:本机所有网络接口都接收连接,局域网设备可能访问到。
例如:
# 只允许本机访问
npm run dev -- --host localhost
# 允许局域网设备访问,适合手机调试
npm run dev -- --host 0.0.0.0
很多框架默认只监听 localhost。所以手机和电脑连着同一个 Wi-Fi,也不一定能访问你的开发服务。要用手机调试,通常需要监听 0.0.0.0,然后用电脑的局域网 IP 访问,例如:
http://192.168.1.23:5173
端口冲突
一个端口同一时刻只能被一个进程监听。如果你启动第二个服务监听同一个端口,就会看到类似错误:
Error: listen EADDRINUSE :::3000
EADDRINUSE 的意思是 address already in use,也就是这个地址和端口已经被占用。
常见原因:
- 上次的开发服务器没有关干净。
- 两个项目使用了同一个默认端口。
- 后台进程仍在运行。
- 系统服务或 Docker 容器占用了端口。
如何排查端口占用
macOS / Linux
查看谁占用 3000 端口:
lsof -i :3000
输出里通常能看到进程名和 PID。终止进程:
kill -9 <PID>
如果想看监听端口,也可以使用:
lsof -nP -iTCP -sTCP:LISTEN
Windows
查看谁占用 3000 端口:
netstat -ano | findstr :3000
根据 PID 终止进程:
taskkill /PID <PID> /F
端口冲突的处理策略
遇到端口冲突,一般有三种处理方式:
| 方式 | 适合场景 | 注意点 |
|---|---|---|
| 杀掉旧进程 | 确认旧服务无用 | 不要误杀数据库、代理、系统服务 |
| 换一个端口 | 多项目同时运行 | 前端代理、回调地址也要同步改 |
| 配置固定端口 | 团队协作或文档固定 | 避免每个人随机端口造成沟通混乱 |
很多现代框架在端口被占用时会询问是否换端口。但如果前端、后端、OAuth 回调、WebSocket 地址已经写死,自动换端口可能会引出新的问题。
同源策略和跨域
浏览器的同源策略(Same-Origin Policy)规定:只有协议、域名、端口都一致,才算同源。
| 地址 A | 地址 B | 是否同源 | 原因 |
|---|---|---|---|
http://localhost:5173 |
http://localhost:5173/about |
是 | 协议、域名、端口都一样 |
http://localhost:5173 |
http://localhost:3000 |
否 | 端口不同 |
http://localhost:5173 |
https://localhost:5173 |
否 | 协议不同 |
http://localhost:5173 |
http://127.0.0.1:5173 |
否 | 域名不同,虽然都指向本机 |
这也是前后端分离项目常见跨域问题的来源:
前端开发服务器: http://localhost:5173
后端 API 服务: http://localhost:3000
端口不同,浏览器就认为它们不同源。
跨域的两种常见解决方案
方案一:后端配置 CORS
后端明确允许前端源访问:
// Express 示例
app.use(cors({
origin: 'http://localhost:5173',
}));
适合后端可控、需要真实跨源访问的情况。
方案二:前端开发服务器代理
在开发环境中,让前端开发服务器转发 API 请求:
// vite.config.js
export default {
server: {
proxy: {
'/api': 'http://localhost:3000',
},
},
};
浏览器请求的是:
http://localhost:5173/api/users
Vite 在服务端把请求转发给:
http://localhost:3000/api/users
浏览器只看到同源请求,所以不会触发跨域限制。
三类常见问题排查
1. 端口被占用
现象:
Error: listen EADDRINUSE :::3000
排查:
lsof -i :3000
处理:
- 杀掉旧进程。
- 换端口。
- 检查 Docker、IDE、后台任务是否还在运行。
2. 连接被拒绝
现象:
ERR_CONNECTION_REFUSED
常见原因:
- 服务没启动。
- 端口写错。
- 服务只监听
localhost,但你从局域网设备访问。 - 防火墙或安全软件阻止连接。
排查顺序:
- 看终端服务是否启动成功。
- 用
lsof -i :端口或netstat确认监听。 - 本机先访问
localhost:端口。 - 局域网访问时确认服务监听
0.0.0.0。 - 检查防火墙和安全组。
3. 跨域被拦截
现象:
Access to fetch at ... from origin ... has been blocked by CORS policy
排查:
- 看浏览器控制台错误。
- 看 Network 面板里的请求地址、状态码和响应头。
- 确认协议、域名、端口是否一致。
- 确认后端是否返回
Access-Control-Allow-Origin。 - 开发环境优先考虑配置代理。
排查口诀
遇到 localhost 问题,可以按这个顺序走:
服务是否启动
-> 端口是否正确
-> 监听地址是否正确
-> 本机能否访问
-> 局域网能否访问
-> 是否跨域
-> 是否被防火墙或代理拦截
不要一上来就猜跨域。很多问题其实是服务没启动、端口写错或监听地址不对。
名词对照表
| 英文术语 | 中文对照 | 解释 |
|---|---|---|
| Port | 端口 | 一个 0-65535 的数字,用来区分同一台机器上的不同网络服务。 |
| localhost | 本地主机 | 特殊域名,通常指向本机环回地址。 |
| Loopback Interface | 环回接口 | 操作系统的虚拟网络接口,发往 127.0.0.1 的数据包不会离开本机。 |
| EADDRINUSE | 地址已被使用 | 表示要监听的端口已经被另一个进程占用。 |
| CORS | 跨域资源共享 | 浏览器安全机制,跨源请求需要服务端明确允许。 |
| Same-Origin Policy | 同源策略 | 浏览器安全基石:协议、域名、端口都一致才算同源。 |
| Proxy | 代理 | 在开发环境中代替浏览器向后端转发请求,常用于绕过跨域限制。 |
0.0.0.0 |
所有接口 | 服务监听所有网络接口,可能允许局域网访问。 |
| Well-known Ports | 知名端口 | 0-1023 端口,预留给 HTTP、HTTPS、SSH 等标准协议。 |
| PID | 进程 ID | 操作系统为每个运行中程序分配的唯一编号。 |
| lsof | 列出打开的文件 | macOS/Linux 下查看端口占用常用命令。 |
| HMR | 热模块替换 | 开发服务器在代码变更后通知浏览器更新页面的机制。 |
小结
- 端口是同一台机器上区分不同服务的编号。
localhost是本机地址,通常解析到127.0.0.1,请求不会离开本机。0.0.0.0是监听所有网络接口,常用于局域网调试,但要注意安全。EADDRINUSE表示端口已经被占用。- 浏览器同源由协议、域名、端口共同决定,端口不同也会跨域。
- 开发环境跨域通常用 CORS 或 dev server proxy 解决。