CC 咖啡猫的工作空间 Coding Space

计算机网络(前端视角)

前端开发必备的网络知识全景图。涵盖 HTTP 协议演进、HTTPS/TLS、TCP/IP、DNS、缓存、跨域、状态管理及网络优化策略。


1. HTTP 协议演进

1.1 HTTP/0.9(1991)

  • 只有 GET 方法
  • 响应只有 HTML,无头部、无状态码
  • 单行协议,一锤子买卖

1.2 HTTP/1.0(1996)

  • 增加 POSTHEAD 方法
  • 引入 HTTP 头部(Content-TypeContent-Length 等)
  • 引入状态码
  • 致命缺陷:每个 TCP 连接只处理一个请求,用完即断

1.3 HTTP/1.1(1997)—— 至今仍广泛使用

特性 说明
持久连接 Connection: keep-alive(默认),复用 TCP 连接,减少三次握手开销
管道化 允许在等待响应时发送后续请求(队头阻塞:响应必须按序返回,前一个堵住后面全堵)
分块传输 Transfer-Encoding: chunked,服务端可边生成边发送,无需 Content-Length
Host 头 支持同一 IP 多个虚拟主机
更多方法 PUTDELETEOPTIONSCONNECTTRACE
缓存控制 Cache-ControlETagIf-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 混合加密方案

  1. 用非对称加密安全交换对称密钥
  2. 用对称密钥加密实际数据传输

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(可多层)
        ↓ 颁发
服务器证书(叶子证书)

证书包含:域名、公钥、有效期、签发者、签名算法、数字签名。

验证过程

  1. 验证证书链可追溯到受信任的根 CA
  2. 验证域名匹配
  3. 验证有效期
  4. 验证撤销状态(CRL / OCSP Stapling)
  5. 用上级证书公钥验证签名

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 原理

  1. 智能 DNS:用户请求域名 → DNS 解析 → 返回距离用户最近的 CDN 节点 IP
  2. 边缘节点:缓存静态资源,未命中则回源站取
  3. 回源策略:透传 / 缓存 / 预热
用户 → 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 被拦截
  • 存储访问:localStorageIndexedDB 隔离

6.2 CORS(跨域资源共享)

简单请求(同时满足):

  • 方法:GETHEADPOST
  • 仅包含安全的请求头(AcceptAccept-LanguageContent-LanguageContent-Type 限于 application/x-www-form-urlencodedmultipart/form-datatext/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. 状态管理

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 到关键第三方源

参考