计算机网络(前端视角)
前端开发必备的网络知识全景图。涵盖 HTTP 协议演进、HTTPS/TLS、TCP/IP、DNS、缓存、跨域、状态管理及网络优化策略。
1. HTTP 协议演进
1.1 HTTP/0.9(1991)
- 只有
GET方法 - 响应只有 HTML,无头部、无状态码
- 单行协议,一锤子买卖
1.2 HTTP/1.0(1996)
- 增加
POST、HEAD方法 - 引入 HTTP 头部(
Content-Type、Content-Length等) - 引入状态码
- 致命缺陷:每个 TCP 连接只处理一个请求,用完即断
1.3 HTTP/1.1(1997)—— 至今仍广泛使用
| 特性 | 说明 |
|---|---|
| 持久连接 | Connection: keep-alive(默认),复用 TCP 连接,减少三次握手开销 |
| 管道化 | 允许在等待响应时发送后续请求(队头阻塞:响应必须按序返回,前一个堵住后面全堵) |
| 分块传输 | Transfer-Encoding: chunked,服务端可边生成边发送,无需 Content-Length |
| Host 头 | 支持同一 IP 多个虚拟主机 |
| 更多方法 | PUT、DELETE、OPTIONS、CONNECT、TRACE |
| 缓存控制 | Cache-Control、ETag、If-None-Match |
队头阻塞(HOL Blocking):
请求: [1] → [2] → [3] → ...
响应: [1] ← [2] ← [3] ← ... # 2 慢则 3 等待
解法:浏览器为每个域名开 6 个左右的 TCP 连接来缓解,但没有根治。
1.4 HTTP/2(2015)
基于 Google SPDY 协议,二进制分帧层替代文本协议。
核心改进:
| 特性 | 原理 | 效果 |
|---|---|---|
| 多路复用 | 单一 TCP 连接内并行交错的请求/响应流 | 消除队头阻塞(应用层) |
| 头部压缩 | HPACK:静态表 + 动态表 + Huffman 编码 | 头体积减少 85%+ |
| 服务器推送 | 服务端主动推送资源(如 HTML 刚请求就推 CSS) | 减少 RTT |
| 流优先级 | 客户端指定资源优先级 | 关键资源先到 |
| 二进制分帧 | 帧(Frame)+ 流(Stream) | 更易解析、更灵活 |
帧结构:
+-----------------------------------------------+
| Length (24) | Type (8) | Flags (8) |
+-----------------------------------------------+
| R | Stream Identifier (31) |
+-----------------------------------------------+
| Frame Payload ... |
+-----------------------------------------------+
缺陷:
- TCP 层的队头阻塞依然存在(TCP 丢包重传会阻塞所有流)
- 连接协商加密成本高
1.5 HTTP/3(2022)
基于 QUIC(Quick UDP Internet Connections)协议。
| 改进点 | 说明 |
|---|---|
| 传输层用 UDP | 彻底消除 TCP 队头阻塞 |
| 0-RTT 握手 | 首次连接 1-RTT,再次连接 0-RTT |
| 连接迁移 | 切换网络(WiFi → 4G)连接不中断(连接 ID 机制) |
| 内置 TLS 1.3 | 加密是强制性的,无明文 HTTP/3 |
| 流级别的流量控制 | 单个流丢包不影响其他流 |
HTTP/1.1: TCP + TLS 1.2 + HTTP/1.1
HTTP/2: TCP + TLS 1.2 + HPACK + HTTP/2
HTTP/3: UDP + QUIC + QPACK + HTTP/3
2. HTTPS 与 TLS
2.1 对称 vs 非对称加密
| 类型 | 原理 | 特点 |
|---|---|---|
| 对称加密 | 同一密钥加密解密(AES、ChaCha20) | 快,但密钥分发困难 |
| 非对称加密 | 公钥加密、私钥解密(RSA、ECDHE) | 慢,但解决密钥分发 |
HTTPS 混合加密方案:
- 用非对称加密安全交换对称密钥
- 用对称密钥加密实际数据传输
2.2 TLS 1.2 握手过程
Client Server
|------- ClientHello -------->| ① 支持 TLS 版本、密码套件、随机数 random_C
|<------ ServerHello --------| ② 选定 TLS 版本、密码套件、随机数 random_S
|<------ Certificate --------| ③ 服务端证书(含公钥)
|<---- ServerHelloDone ------| ④ 告知完成
|--- ClientKeyExchange ------>| ⑤ 用服务器公钥加密 PreMasterSecret
|--- ChangeCipherSpec ------->| ⑥ 后续使用对称密钥
|--- Finished --------------->| ⑦ 握手消息 MAC 验证
|<-- ChangeCipherSpec --------| ⑧ 服务端也要切换
|<-- Finished ----------------| ⑨ 握手完成,开始应用数据
完整握手 2-RTT,之后复用 Session 可缩减为 1-RTT。
2.3 TLS 1.3 握手过程
Client Server
|--- ClientHello + KeyShare -->| ① 直接带密钥分享参数和猜测的密码套件
|<-- ServerHello + KeyShare ---| ② 选定参数,计算共享密钥
|<-- Certificate + Finished ---| ③ 证书链 + 握手完成
|--- Finished ---------------->| ④ 握手完成
1-RTT 完整握手,重连 0-RTT(需服务端支持 early data)。 删除了不安全套件(RSA 密钥交换、RC4、3DES、CBC 模式等)。
2.4 证书链与 CA
浏览器信任根 CA
↓ 颁发
中间 CA(可多层)
↓ 颁发
服务器证书(叶子证书)
证书包含:域名、公钥、有效期、签发者、签名算法、数字签名。
验证过程:
- 验证证书链可追溯到受信任的根 CA
- 验证域名匹配
- 验证有效期
- 验证撤销状态(CRL / OCSP Stapling)
- 用上级证书公钥验证签名
2.5 中间人攻击(MITM)
原理:
正常连接:客户端 ←→ 服务端(加密通道)
MITM: 客户端 ←→ 攻击者 ←→ 服务端
伪证书 真实连接
防御:
- 客户端校验证书(证书透明度 CT)
- HSTS 头强制 HTTPS
- 证书固定(HPKP,已废弃,用 Expect-CT 替代)
- 不要信任非官方根证书
3. TCP/IP
3.1 三次握手(建立连接)
Client Server
|---- SYN (seq=x) --------->| ① SYN-SENT
|<-- SYN+ACK (seq=y, ack=x+1) | ② SYN-RECEIVED
|---- ACK (seq=x+1, ack=y+1) ->| ③ ESTABLISHED
为什么是三次?:防止已失效的连接请求到达服务器。两次握手会导致服务端误开连接,浪费资源。
SYN 洪水攻击:攻击者大量发送 SYN 但不回应 ACK,耗尽服务端半连接队列(半连接队列 → syncookie 防御)。
3.2 四次挥手(断开连接)
Client Server
|---- FIN (seq=u) --------->| ① FIN-WAIT-1
|<-- ACK (seq=v, ack=u+1) --| ② CLOSE-WAIT(客户端进入 FIN-WAIT-2)
|<-- FIN (seq=w) -----------| ③ LAST-ACK(半关闭状态)
|---- ACK (seq=u+1, ack=w+1)->| ④ TIME-WAIT(2MSL 后关闭)
为什么是四次?:服务端收到 FIN 后可能还有数据要发,先回 ACK,等数据发完再发 FIN。
TIME-WAIT 为什么是 2MSL?:确保最后一个 ACK 能到达,且本连接的所有报文在网络中过期消失。
状态转换图:
CLOSED → SYN-SENT → ESTABLISHED → FIN-WAIT-1 → FIN-WAIT-2 → TIME-WAIT → CLOSED
↓ ↓
CLOSE-WAIT → LAST-ACK → CLOSED
3.3 流量控制
滑动窗口:接收方通过 TCP 头中的 Window Size 字段告知发送方自己的接收能力。
发送方: |已发送已确认|已发送未确认|可发送|不可发送|
接收方窗口(rwnd): |已接收|空闲缓冲区大小|
- 接收方窗口缩小至 0 时,发送方停止发送,定期发送 窗口探测(Window Probe)
- 糊涂窗口综合征:接收方只释放少量窗口 → 发送方发小包 → 用 Nagle 算法 + Clark 方案解决
3.4 拥塞控制
拥塞窗口(cwnd)与接收窗口(rwnd)的较小值为实际发送窗口。
四种算法:
| 算法 | 触发时机 | 行为 |
|---|---|---|
| 慢启动 | 连接建立 / 超时重传 | cwnd 从 1 开始,每收到 ACK 指数增长(×2),直到 ssthresh |
| 拥塞避免 | cwnd ≥ ssthresh | cwnd 线性增长(+1/RTT),直到丢包 |
| 快速重传 | 收到 3 个重复 ACK | 立即重传丢失报文,不等待超时 |
| 快速恢复 | 快速重传后 | cwnd 减半,进入拥塞避免而非慢启动 |
cwnd
↑
| ___________
| / 拥塞避免阶段
| / (线性增长)
| / ssthresh
|/ 慢启动
| (指数增长)
+----------------------→ 时间
丢包 ↓
cwnd = 上次丢包时 cwnd × 0.5(快速恢复)
BBR(Bottleneck Bandwidth and Round-trip propagation time):Google 开发的基于模型而非丢包的拥塞控制算法,不将丢包视为拥塞信号,更适合高带宽高延迟链路。
4. DNS
4.1 解析过程
递归查询(用户侧 DNS 服务器代为查询):
客户端 → 本地 DNS 服务器 → 根域名服务器 → 顶级域名服务器 → 权威域名服务器
迭代查询(DNS 服务器让客户端自己查):
客户端 → 本地 DNS 服务器
└→ 根服务器(返回 .com 的 NS)
└→ .com 服务器(返回 example.com 的 NS)
└→ example.com 权威服务器(返回 A 记录)
完整流程:
浏览器缓存 → 操作系统缓存 → hosts 文件 → 本地 DNS 服务器 → 根 → TLD → 权威
4.2 DNS 记录类型
| 类型 | 说明 |
|---|---|
| A | IPv4 地址 |
| AAAA | IPv6 地址 |
| CNAME | 域名别名(指向另一个域名) |
| MX | 邮件交换记录 |
| NS | 域名服务器 |
| TXT | 文本记录(SPF、DKIM、验证域名所有权) |
4.3 DNS 预解析
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="//api.example.com">
<!-- 更激进的预解析(同时预连接) -->
<link rel="preconnect" href="https://api.example.com">
<!-- 页面内所有 a 标签自动预解析 -->
<meta http-equiv="x-dns-prefetch-control" content="on">
4.4 CDN 原理
- 智能 DNS:用户请求域名 → DNS 解析 → 返回距离用户最近的 CDN 节点 IP
- 边缘节点:缓存静态资源,未命中则回源站取
- 回源策略:透传 / 缓存 / 预热
用户 → DNS → 最近的 CDN 节点 IP
↓
CDN 节点有缓存?→ 直接返回
↓ 无
回源服务器获取 → 缓存到节点 → 返回
CDN 缓存策略:遵循源站的 Cache-Control,可通过 CDN 控制台配置覆盖。
5. 缓存机制
5.1 强缓存(不发起 HTTP 请求)
| 头部 | 说明 | 优先级 |
|---|---|---|
Expires |
HTTP/1.0,绝对时间,依赖客户端时间 | 低(被 Cache-Control 覆盖) |
Cache-Control: max-age=3600 |
HTTP/1.1,相对时间 | 高 |
其他 Cache-Control 指令:
Cache-Control: public # 可被任何缓存缓存
Cache-Control: private # 仅浏览器可缓存
Cache-Control: no-cache # 使用前需验证
Cache-Control: no-store # 完全不缓存
Cache-Control: must-revalidate # 过期后必须验证
Cache-Control: immutable # 永不过期(配合 max-age)
强缓存命中:状态码 200 (from disk cache) 或 200 (from memory cache)。
5.2 协商缓存(需要验证)
| 请求头 | 响应头 | 原理 |
|---|---|---|
If-Modified-Since |
Last-Modified |
文件的最后修改时间(精度秒,可能不准) |
If-None-Match |
ETag |
文件哈希(强 ETag 内容变更才变,弱 ETag W/"hash" 语义等价即可) |
流程:
请求 → 服务端对比 ETag / Last-Modified
├─ 未变更 → 304 Not Modified(无需下载资源)
└─ 已变更 → 200 + 新资源
优先级:ETag > Last-Modified(ETag 更精确,可解决秒级精度问题)
5.3 缓存策略最佳实践
| 资源类型 | 策略 | 原因 |
|---|---|---|
| HTML | no-cache |
内容频繁更新 |
| CSS/JS 入口文件 | no-cache |
配合文件名 hash |
| 带 hash 的 CSS/JS | max-age=31536000, immutable |
内容不变 URL 不变,可用一年 |
| 图片/字体 | max-age=31536000 |
基本不变 |
| API 响应 | 视业务定(private, max-age=60) |
个性化 + 短期可缓存 |
缓存更新方案:
// 文件名 hash(webpack/Rollup 产物)
main.a1b2c3.js ← 内容变了 hash 变了 → 新文件名
style.xyz789.css
// 版本号 URL(不推荐,缓存失效成本高)
/main?v=2
6. 跨域
6.1 同源策略
同源定义:协议 + 域名 + 端口 三者相同。
https://example.com:443
↑ ↑ ↑
协议 域名 端口
限制:
- DOM 访问:不同源的
iframe/ 窗口不能互相操作 DOM - 数据请求:不同源的
XMLHttpRequest/fetch被拦截 - 存储访问:
localStorage、IndexedDB隔离
6.2 CORS(跨域资源共享)
简单请求(同时满足):
- 方法:
GET、HEAD、POST - 仅包含安全的请求头(
Accept、Accept-Language、Content-Language、Content-Type限于application/x-www-form-urlencoded、multipart/form-data、text/plain)
简单请求直接发,浏览器检查响应头:
Access-Control-Allow-Origin: https://example.com # 或 *
Access-Control-Allow-Credentials: true
预检请求(Preflight):
非简单请求先发 OPTIONS 请求:
OPTIONS /api/data HTTP/1.1
Origin: https://frontend.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
服务端响应:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://frontend.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Max-Age: 86400 # 预检缓存时间
Access-Control-Allow-Credentials: true # 允许携带凭证
前端带凭证:
fetch('https://api.example.com/data', {
credentials: 'include', // 携带 cookie
// 或 mode: 'cors'
});
注意:带凭证时 Access-Control-Allow-Origin 不能为 *。
6.3 JSONP
原理:<script> 标签不受同源策略限制。
// 前端
function handleData(data) {
console.log(data);
}
const script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleData';
document.head.appendChild(script);
// 服务端返回
handleData({ id: 1, name: 'test' });
缺点:只支持 GET,有安全风险(可被注入恶意代码),无错误处理。
6.4 postMessage
跨窗口/iframe 通信:
// 发送方
targetWindow.postMessage({ type: 'UPDATE', payload: data }, 'https://other.com');
// 接收方
window.addEventListener('message', (event) => {
// 必须验证来源!
if (event.origin !== 'https://other.com') return;
console.log(event.data);
});
6.5 代理转发
# Nginx 反向代理解决跨域
server {
listen 80;
server_name frontend.com;
location /api/ {
proxy_pass https://api.backend.com/;
proxy_set_header Host api.backend.com;
proxy_set_header X-Real-IP $remote_addr;
}
}
// Webpack Dev Server 代理
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'https://api.backend.com',
changeOrigin: true,
}
}
}
};
7. 状态管理
7.1 Cookie
Cookie 属性:
| 属性 | 说明 |
|---|---|
HttpOnly |
禁止 JS 访问(document.cookie),防 XSS 窃取 |
Secure |
仅在 HTTPS 下传输 |
SameSite=Strict |
完全禁止跨站发送 |
SameSite=Lax |
大多数跨站不发送(默认值,除导航到目标站点的 GET 请求) |
SameSite=None |
跨站也发送(必须配合 Secure) |
Domain |
指定哪些域名可用(包含子域名) |
Path |
指定哪些路径可用 |
Max-Age / Expires |
过期时间 |
Partitioned |
[CHIPS] 每个顶级站点独立分区存储 |
Cookie 大小限制:单个 Cookie ≤ 4KB,每个域名 ≤ 20 个(浏览器差异)。
7.2 Session
服务端存储的会话数据:
客户端 Cookie: connect.sid=s%3Axxx.xxx
↓
服务端: sessionId → Redis/内存中的会话数据
优势:服务端主动失效;劣势:扩展需共享 Session(Redis 集中存储)。
7.3 Token / JWT
JWT 结构:
header.payload.signature
// Header
{ "alg": "HS256", "typ": "JWT" }
// Payload
{
"sub": "user123",
"name": "张三",
"iat": 1516239022,
"exp": 1516242622
}
// Signature
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
JWT 工作流程:
POST /login
→ 200 { "token": "eyJhbGciOiJIUzI1NiIs..." }
GET /api/user
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
JWT vs Session:
| 特性 | Session | JWT |
|---|---|---|
| 存储位置 | 服务端 | 客户端 |
| 扩展性 | 需共享 Session 存储 | 天然无状态 |
| 失效 | 可服务端主动踢 | 需黑名单 / 短过期 + refresh token |
| 体积 | 小 Cookie | 较大(payload 信息多) |
| 安全 | 依赖 Cookie 机制 | 签名防篡改,但 payload 明文 |
JWT 最佳实践:
access_token短过期(15min)+refresh_token长过期(7d)- 敏感信息不要放在 payload(它是 base64 编码而非加密)
- 使用
RS256替代HS256(非对称签名,服务端不存密钥) - Token 刷新时使用 Token Rotation
8. 网络优化
8.1 连接复用
- HTTP Keep-Alive:复用 TCP 连接
- HTTP/2 多路复用:单连接并行请求
- Connection Pool:浏览器会维护连接池,复用空闲连接
- Domain Sharding(过时):HTTP/1.1 时代通过多域名突破连接数限制,HTTP/2 下有害(增加 DNS 和连接数)
8.2 资源预加载
<!-- preload:当前页面立即需要的资源(高优先级) -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="font.woff2" as="font" crossorigin>
<link rel="preload" href="main.js" as="script">
<!-- prefetch:将来页面可能需要的资源(低优先级) -->
<link rel="prefetch" href="next-page.js" as="script">
<!-- preconnect:提前建立连接(DNS + TCP + TLS) -->
<link rel="preconnect" href="https://api.example.com">
<!-- dns-prefetch:仅提前解析 DNS,兼容性更广 -->
<link rel="dns-prefetch" href="https://api.example.com">
<!-- prerender:预测用户即将访问的页面,完全预渲染(谨慎使用) -->
<link rel="prerender" href="https://example.com/next">
优先级:preload > preconnect > dns-prefetch > prefetch
8.3 资源压缩
| 算法 | 说明 | 浏览器支持 |
|---|---|---|
| Gzip | 最广泛,CPU 友好,压缩比 70%+ | 全部支持 |
| Brotli | Google 开发,比 gzip 高 20-30% 压缩比 | 现代浏览器全面支持 |
# Nginx 配置 Brotli(需安装 brotli 模块)
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/json application/javascript image/svg+xml;
# 同时保留 gzip 作为降级
gzip on;
gzip_types text/plain text/css application/json application/javascript;
最佳实践:
- 文本类资源(HTML/CSS/JS/JSON/SVG)必须压缩
- 图片/视频一般不压缩(已有自身压缩)
- Brotli 等级 4-6 性价比最高(11 级压缩更好但更慢)
- 小文件(<1KB)不压缩(压缩后可能更大 + 解压开销)
8.4 其他优化手段
| 策略 | 方案 |
|---|---|
| HTTP/2 Server Push | 服务端推送关键资源(注意:已被 103 Early Hints 趋势替代) |
| 103 Early Hints | 在 200 之前先返回 103 状态码提示浏览器预加载资源 |
| 图片优化 | WebP/AVIF、响应式图片 srcset、懒加载 loading="lazy" |
| Service Worker | 缓存策略优先从 SW 缓存获取(Cache-First / Network-First) |
| 资源内联 | 小 CSS/JS 内联到 HTML(减少请求数) |
| CDN 预热 | 大版本上线前提前刷热 CDN 节点 |
| 关键 CSS | 首屏 CSS 内联,剩余异步加载 |
| 预建连接 | 提前 preconnect 到关键第三方源 |