前端网络知识指南
一、HTTP 核心知识
1.1 HTTP 报文结构
1.1.1 请求报文结构
javascript
复制代码
// HTTP 请求报文示例
/*
请求行(Request Line)
GET /api/users HTTP/1.1
请求头(Headers)
Host: api.example.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
Accept: application/json
Content-Type: application/json
Content-Length: 56
Connection: keep-alive
Cache-Control: no-cache
空行(CRLF)
请求体(Body)- 可选
{"name": "Alice", "age": 25}
*/
// 1. 请求行组成:
// - 方法(Method):GET、POST、PUT、DELETE、PATCH 等
// - 请求 URL:路径和查询参数
// - HTTP 版本:HTTP/1.1 或 HTTP/2
// 2. 请求头(Headers)组成:
// - Host:目标服务器域名和端口
// - User-Agent:客户端信息(浏览器、操作系统)
// - Accept:客户端可接受的响应格式
// - Content-Type:请求体的格式
// - Content-Length:请求体字节数
// - Authorization:认证信息
// - Cookie:发送给服务器的 Cookie
// 3. 空行:必须有一个空行,标识请求头结束
// 4. 请求体:POST/PUT 请求时携带的数据
1.1.2 响应报文结构
javascript
复制代码
// HTTP 响应报文示例
/*
状态行(Status Line)
HTTP/1.1 200 OK
响应头(Headers)
Server: nginx/1.18.0
Date: Mon, 12 May 2026 10:30:00 GMT
Content-Type: application/json
Content-Length: 128
Connection: keep-alive
Set-Cookie: sessionId=abc123; Path=/; HttpOnly
Cache-Control: max-age=3600
空行(CRLF)
响应体(Body)
{"code": 0, "data": {"id": 1, "name": "Alice"}}
*/
// 1. 状态行组成:
// - HTTP 版本:HTTP/1.1 或 HTTP/2
// - 状态码:200、404、500 等
// - 状态文本:OK、Not Found、Internal Server Error
// 2. 响应头:
// - Server:服务器信息
// - Date:响应时间
// - Content-Type:响应体格式
// - Content-Length:响应体字节数
// - Set-Cookie:设置 Cookie
// - Cache-Control:缓存控制
// 3. 常见状态码:
// - 1xx:信息性状态(继续、切换协议)
// - 2xx:成功(200 OK、201 Created、204 No Content)
// - 3xx:重定向(301 Moved、302 Found、304 Not Modified)
// - 4xx:客户端错误(400 Bad Request、401 Unauthorized、403 Forbidden、404 Not Found)
// - 5xx:服务器错误(500 Internal Server Error、502 Bad Gateway、503 Service Unavailable)
1.2 HTTP 方法详解
javascript
复制代码
// HTTP 方法对比
// 1. GET:获取资源
// - 请求参数在 URL 中(查询字符串)
// - 用于获取数据,不应修改服务器状态
// - 浏览器会缓存 GET 请求
// - 有长度限制(约 2KB)
fetch('/api/users?id=1', {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
// 2. POST:创建资源
// - 请求参数在请求体中
// - 用于提交数据,可能修改服务器状态
// - 不会被浏览器缓存
// - 没有长度限制(或限制很大)
fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'Alice', age: 25 })
});
// 3. PUT:更新资源(完整更新)
// - 替换整个资源,所有字段必须提供
// - 幂等性:多次 PUT 结果相同
fetch('/api/users/1', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'Alice', age: 26 })
});
// 4. PATCH:部分更新资源
// - 只更新部分字段
// - 非幂等:多次 PATCH 结果可能不同
fetch('/api/users/1', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ age: 26 })
});
// 5. DELETE:删除资源
// - 删除指定的资源
// - 幂等性:多次 DELETE 结果相同(都是删除)
fetch('/api/users/1', {
method: 'DELETE'
});
// 6. HEAD:获取响应头
// - 与 GET 类似,但不返回响应体
// - 用于检查资源是否存在或获取元信息
fetch('/api/users/1', {
method: 'HEAD'
});
// 7. OPTIONS:获取支持的 HTTP 方法
// - 用于 CORS 预检请求
// - 返回服务器支持的 HTTP 方法列表
fetch('/api/users', {
method: 'OPTIONS',
headers: {
'Access-Control-Request-Method': 'POST',
'Access-Control-Request-Headers': 'Content-Type'
}
});
1.3 HTTP 持久连接与管线化
javascript
复制代码
// 1. 持久连接(Keep-Alive)
// HTTP/1.1 默认使用持久连接
// 多个请求可以共用一个 TCP 连接,避免频繁建立连接
// 1.1 Connection: keep-alive
// 请求头中声明使用持久连接
// 服务器返回响应后,不会立即关闭 TCP 连接
// 1.2 连接管理
const http = require('http');
// 创建 HTTP 服务器
const server = http.createServer((req, res) => {
// 不设置 Connection: close,默认为 keep-alive
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Hello' }));
// 打印连接信息
console.log('Connection:', req.headers.connection);
});
server.listen(3000);
// 1.3 持久连接超时
// 超过一定时间没有新请求,服务器主动关闭连接
// 服务器设置:Keep-Alive: timeout=5, max=1000
// timeout:超时时间(秒)
// max:最大请求数
// 2. 管线化(Pipelining)
// 客户端可以不等响应,连续发送多个请求
// 服务器按顺序返回响应(队头阻塞问题)
// 2.1 管线化请求示例
// 请求1: GET /api/users
// 请求2: GET /api/posts (不等待响应1,直接发送)
// 请求3: GET /api/comments
// 响应顺序必须与请求顺序一致
// 如果响应1很慢,响应2和响应3会等待
// 3. HTTP/2 多路复用
// 解决 HTTP/1.1 管线化的队头阻塞问题
// 在一个 TCP 连接中,可以并发传输多个请求和响应
// 3.1 HTTP/2 帧结构
/*
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
| R | Stream Identifier (31) |
+-+-------------------------------------------------------------+
| Frame Payload (0...) |
+---------------------------------------------------------------+
*/
// - Length:帧长度(24 位,最大 16MB)
// - Type:帧类型(HEADERS、DATA、SETTINGS 等)
// - Flags:标志位(END_STREAM、END_HEADERS 等)
// - Stream Identifier:流 ID(31 位,用于多路复用)
// 4. 连接对比
const connectionComparison = {
'HTTP/1.0': {
'持久连接': '不支持,每次请求都建立新连接',
'管线化': '不支持',
'并发': '需要多个连接(浏览器通常限制 6-8 个)'
},
'HTTP/1.1': {
'持久连接': '支持,默认开启',
'管线化': '支持,但不常用(队头阻塞)',
'并发': '需要多个连接'
},
'HTTP/2': {
'持久连接': '支持,只能有一个连接',
'管线化': '不支持(用多路复用替代)',
'并发': '多路复用,在一个连接中并发传输'
},
'HTTP/3': {
'持久连接': '基于 UDP,不存在连接概念',
'管线化': '支持(QUIC 层面解决队头阻塞)',
'并发': '真正的并发,无队头阻塞'
}
};
二、HTTPS 与 TLS
2.1 HTTPS 加密原理
javascript
复制代码
// 1. HTTPS 工作流程
/*
TLS 握手过程(简化版):
1. 客户端 -> 服务器:ClientHello
- 客户端支持的 TLS 版本
- 支持的加密套件列表
- 客户端随机数(Client Random)
2. 服务器 -> 客户端:ServerHello
- 确定使用的 TLS 版本和加密套件
- 服务器随机数(Server Random)
- 服务器证书(包含公钥)
3. 客户端验证证书
- 检查证书是否由可信 CA 签发
- 检查证书是否过期
- 检查域名是否匹配
4. 客户端 -> 服务器:Pre-Master Secret
- 生成另一个随机数(Pre-Master Secret)
- 使用服务器公钥加密后发送
- 结合三个随机数生成对称密钥
5. 客户端 -> 服务器:ChangeCipherSpec
- 告诉服务器后续通信使用加密
6. 客户端 -> 服务器:Finished
- 加密验证消息(之前所有握手消息的摘要)
7. 服务器 -> 客户端:ChangeCipherSpec
- 告诉客户端后续通信使用加密
8. 服务器 -> 客户端:Finished
- 加密验证消息
后续通信使用对称加密(AES)加密数据
*/
// 2. 对称加密 vs 非对称加密
// 2.1 对称加密
// 加密和解密使用相同的密钥
// 优点:速度快,适合加密大量数据
// 缺点:密钥传输不安全
const crypto = require('crypto');
function symmetricEncrypt(data, key) {
// 生成初始向量(IV)
const iv = Buffer.alloc(16, 0);
// 创建 cipher
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
return { iv: iv.toString('hex'), encrypted };
}
function symmetricDecrypt(encrypted, key, iv) {
const decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.from(iv, 'hex'));
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// 2.2 非对称加密
// 公钥加密,私钥解密(或私钥加密,公钥解密)
// 优点:密钥传输安全
// 缺点:速度慢,不适合加密大量数据
function generateKeyPair() {
const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048, // 密钥长度
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
return { publicKey, privateKey };
}
function rsaEncrypt(data, publicKey) {
const buffer = Buffer.from(data);
const encrypted = crypto.publicEncrypt(
{ key: publicKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
buffer
);
return encrypted.toString('base64');
}
function rsaDecrypt(encrypted, privateKey) {
const buffer = Buffer.from(encrypted, 'base64');
const decrypted = crypto.privateDecrypt(
{ key: privateKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING },
buffer
);
return decrypted.toString('utf8');
}
// 2.3 HTTPS 使用的混合方案
/*
1. 使用非对称加密传输对称密钥(解决密钥传输问题)
2. 使用对称加密加密实际数据(利用速度优势)
*/
// 3. 证书验证
const https = require('https');
const fs = require('fs');
// 创建 HTTPS 服务器
const options = {
// 1. 服务器证书(含公钥)
cert: fs.readFileSync('/path/to/cert.pem'),
// 2. 私钥(用于解密客户端发送的 pre-master secret)
key: fs.readFileSync('/path/to/key.pem'),
// 3. CA 证书(可选,用于验证客户端证书)
ca: fs.readFileSync('/path/to/ca.pem'),
// 4. 要求客户端证书(可选)
requestCert: true,
// 5. 忽略证书错误(仅用于测试)
rejectUnauthorized: false
};
const server = https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('HTTPS secured');
});
server.listen(443);
// 4. TLS 版本演进
const tlsVersions = {
'SSL 2.0': { year: 1995, status: '废弃', issues: '已知安全漏洞' },
'SSL 3.0': { year: 1996, status: '废弃', issues: 'POODLE 攻击' },
'TLS 1.0': { year: 1999, status: '废弃', issues: 'BEAST 攻击' },
'TLS 1.1': { year: 2006, status: '废弃', issues: '已知安全漏洞' },
'TLS 1.2': { year: 2008, status: '推荐', features: 'AEAD、SHA-256、AES-GCM' },
'TLS 1.3': { year: 2018, status: '最新', features: '1-RTT、0-RTT、前向保密' }
};
2.2 数字证书与 CA
javascript
复制代码
// 1. 证书结构(X.509)
/*
证书内容:
- 版本(Version)
- 序列号(Serial Number)
- 签名算法(Signature Algorithm)
- 颁发者(Issuer)
- 有效期(Validity)
- 主体(Subject)- 网站信息
- 公钥(Subject Public Key Info)
- 扩展(Extensions)
- 签名(Signature)
签名 = Hash(证书内容) + CA 私钥加密
*/
// 2. 证书类型
// 2.1 域名验证证书(DV)
// - 只验证域名所有权
// - 颁发速度快,几分钟到几小时
// - 适用于个人网站、博客
// 2.2 组织验证证书(OV)
// - 验证域名所有权 + 组织身份
// - 需要人工验证组织信息
// - 适用于企业网站
// 2.3 扩展验证证书(EV)
// - 最严格的验证
// - 地址栏显示绿色公司名称
// - 适用于金融、电商等高信任需求
// 3. 证书链
/*
浏览器验证证书时,会检查完整的证书链:
根证书(Root CA)
|
v
中间证书(Intermediate CA)
|
v
服务器证书(Server Certificate)
浏览器内置根证书(可信 CA)
-> 验证中间证书(用根证书公钥)
-> 验证服务器证书(用中间证书公钥)
*/
// 4. 使用 OpenSSL 生成自签名证书(仅用于测试)
/*
# 生成私钥
openssl genrsa -out server.key 2048
# 生成 CSR(证书签名请求)
openssl req -new -key server.key -out server.csr
# 使用私钥生成自签名证书
openssl x509 -req -in server.csr -signkey server.key -out server.crt
# 或者一步到位(测试用)
openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout server.key -out server.crt -days 365
*/
// 5. 证书格式转换
/*
# PEM 转 DER(二进制)
openssl x509 -in cert.pem -outform DER -out cert.der
# DER 转 PEM
openssl x509 -inform DER -in cert.der -outform PEM -out cert.pem
# 查看证书信息
openssl x509 -in cert.pem -text -noout
# 验证证书过期时间
openssl x509 -in cert.pem -noout -dates
# 检查证书链
openssl verify -CAfile ca.pem server.crt
*/
// 6. 证书固定(Certificate Pinning)
// 防止 CA 被劫持后签发伪造证书
function certificatePinning() {
// 在客户端预先存储证书或公钥的哈希
const pinnedCertificates = [
'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', // 主证书
'sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=' // 备份证书
];
// 验证时比较证书哈希
return function verifyCertificate(certificate) {
const certHash = calculateHash(certificate);
if (pinnedCertificates.includes(certHash)) {
return true;
}
// 如果是中间证书,验证证书链
return verifyCertificateChain(certificate, pinnedCertificates);
};
}
三、DNS 与域名解析
3.1 DNS 解析流程
javascript
复制代码
// 1. DNS 层级结构
/*
根域名服务器(Root Server)
|
v
顶级域服务器(.com、.org、.cn 等)
|
v
权威域名服务器(example.com)
|
v
主机记录(www.example.com)
全球共有 13 组根域名服务器(A-M)
*/
// 2. DNS 解析流程
/*
1. 检查浏览器 DNS 缓存
2. 检查操作系统 DNS 缓存(hosts 文件)
3. 检查路由器 DNS 缓存
4. 发送 DNS 查询到 ISP DNS 服务器(递归查询)
5. ISP DNS 服务器查询根域名服务器
6. 根域名服务器返回 .com 顶级域服务器地址
7. 查询 .com 顶级域服务器
8. .com 顶级域服务器返回 example.com 权威服务器地址
9. 查询 example.com 权威服务器
10. 返回目标 IP 地址
*/
// 3. DNS 记录类型
const dnsRecordTypes = {
'A': {
'作用': '将域名映射到 IPv4 地址',
'示例': 'example.com -> 93.184.216.34',
'使用场景': '最常见的 DNS 记录'
},
'AAAA': {
'作用': '将域名映射到 IPv6 地址',
'示例': 'example.com -> 2606:2800:220:1::248:1893',
'使用场景': 'IPv6 环境'
},
'CNAME': {
'作用': '将域名别名映射到另一个域名',
'示例': 'www.example.com -> example.com',
'使用场景': 'CDN 加速、多域名同站点'
},
'MX': {
'作用': '指定邮件服务器地址',
'示例': 'example.com -> mail.example.com',
'优先级': '数值越小优先级越高'
},
'TXT': {
'作用': '存储文本信息',
'示例': '用于 SPF、DKIM 验证',
'使用场景': '邮件安全、反垃圾邮件'
},
'NS': {
'作用': '指定域名服务器',
'示例': 'example.com -> ns1.example.com',
'使用场景': '子域名授权'
},
'SOA': {
'作用': '权威记录起始',
'包含': '主服务器、邮箱、序列号、刷新时间等'
}
};
// 4. DNS 缓存时间(TTL)
// TTL 值越大,缓存时间越长,但更新生效越慢
// 通常设置:TTL = 3600(1小时)或 86400(1天)
// 5. DNS 协议
const dnsProtocol = {
'DNS over UDP': {
'端口': 53,
'优点': '速度快,开销小',
'限制': '单个包限制 512 字节',
'使用场景': '大多数 DNS 查询'
},
'DNS over TCP': {
'端口': 53,
'优点': '支持大包传输',
'使用场景': 'Zone Transfer、大数据包响应'
},
'DNS over HTTPS (DoH)': {
'端口': 443,
'优点': '加密、防劫持、绕过审查',
'使用场景': '隐私保护'
},
'DNS over TLS (DoT)': {
'端口': 853,
'优点': '加密、防劫持',
'使用场景': '隐私保护'
}
};
// 6. JavaScript 中使用 DNS(Node.js)
const dns = require('dns');
function resolveDNS() {
// 6.1 普通解析(使用系统 DNS 缓存)
dns.resolve('example.com', (err, addresses) => {
if (err) console.error(err);
else console.log('IP:', addresses);
});
// 6.2 查询 A 记录
dns.resolve4('example.com', (err, addresses) => {
console.log('IPv4:', addresses);
});
// 6.3 查询 AAAA 记录
dns.resolve6('example.com', (err, addresses) => {
console.log('IPv6:', addresses);
});
// 6.4 反向解析(IP -> 域名)
dns.reverse('93.184.216.34', (err, hostnames) => {
console.log('Hostnames:', hostnames);
});
// 6.5 使用 Promise
const { promisify } = require('util');
const resolve4Promise = promisify(dns.resolve4);
resolve4Promise('example.com').then(addresses => {
console.log('IP:', addresses);
});
}
// 7. 浏览器 DNS 预解析
// <link rel="dns-prefetch" href="//api.example.com">
// 8. 浏览器预连接
// <link rel="preconnect" href="https://api.example.com">
// 同时进行 DNS 解析 + TCP 握手 + TLS 握手
3.2 CDN 原理
javascript
复制代码
// 1. CDN 工作原理
/*
用户请求 -> CDN 边缘节点 -> 缓存命中返回
|
v
缓存未命中 -> 回源(回源服务器获取资源)
|
v
缓存资源 -> 返回给用户 -> 边缘节点缓存
*/
// 2. CDN 关键技术
// 2.1 就近访问
// 用户被引导到最近的边缘节点
// 通过 DNS 智能解析实现
// 2.2 缓存策略
// 边缘节点缓存静态资源
// 缓存头:Cache-Control、Expires、ETag、Last-Modified
// 2.3 动态加速
// 对于动态内容,使用路由优化、协议优化
// 避免链路不稳定导致的延迟
// 3. CDN 缓存失效(Purge)
// 手动刷新:API 调用或管理后台
// 自动失效:根据 TTL 自动过期
// 4. CDN 调度策略
const cdnScheduling = {
'DNS 智能解析': {
'原理': '根据用户 IP 返回最近节点的 IP',
'优点': '简单易实现',
'缺点': '依赖 DNS 服务器准确性'
},
'HTTP 重定向': {
'原理': '返回 302 指向最近节点',
'优点': '精确控制',
'缺点': '多一次 HTTP 请求'
},
'Anycast': {
'原理': '同一 IP 映射到多个物理位置',
'优点': '路由层自动选最近',
'缺点': '需要网络层面支持'
}
};
// 5. CDN 配置示例(以 Cloudflare 为例)
/*
CNAME 记录:
www.example.com -> example.com.cdn.cloudflare.com
或者 A 记录(指定 IP):
www.example.com -> 203.0.113.1
缓存规则:
- 静态资源(.js、.css、.png)缓存 30 天
- HTML 不缓存(或短期缓存)
- API 不缓存
*/
// 6. CDN 回源配置
const originConfig = {
'回源协议': 'HTTP 或 HTTPS',
'回源 HOST': '源站域名或 IP',
'回源路径': '/path 或 /',
'回源超时': {
'连接': '5 秒',
'读取': '30 秒'
},
'回源重试': '2 次'
};
// 7. 前端静态资源优化
// 7.1 使用 CDN 加载第三方库
// <script src="https://cdn.example.com/react/18.2.0/react.production.min.js">
// 7.2 资源打包策略
// - 大库用 CDN(react、vue、lodash)
// - 小库打包(业务代码)
// 7.3 版本控制
// - 使用具体版本号,不使用 latest
// - 利用 cache-control 设置长期缓存
// - 使用 contenthash 做文件名哈希
四、TCP/IP 与网络协议
4.1 TCP 三次握手与四次挥手
javascript
复制代码
// 1. TCP 三次握手(建立连接)
/*
客户端 服务器
| |
| ---- SYN=1, seq=x ----------------> | 客户端发送 SYN(同步请求)
| |
| <--- SYN=1, ACK=1, seq=y, ack=x+1 -- | 服务器返回 SYN + ACK
| |
| ---- ACK=1, seq=x+1, ack=y+1 -------> | 客户端发送 ACK
| |
| 建立连接完成 |
| |
*/
// 1.1 握手状态变化
const tcpHandshake = {
'CLOSED': '无连接',
'SYN_SENT': '已发送 SYN,等待 ACK(客户端)',
'SYN_RECEIVED': '已收到 SYN,发送 SYN+ACK(服务器)',
'ESTABLISHED': '连接建立完成,双方可以传输数据'
};
// 1.2 为什么需要三次?
/*
- 第一次:客户端 -> 服务器(服务器知道客户端能发送)
- 第二次:服务器 -> 客户端(客户端知道服务器能接收、能发送)
- 第三次:客户端 -> 服务器(服务器知道客户端能接收)
三次是最小满足可靠传输的次数
*/
// 1.3 握手参数
const tcpHandshakeParams = {
'SYN': {
'作用': '同步序列号',
'值': '1 表示这是一个连接请求'
},
'seq (序列号)': {
'作用': '标识发送的数据字节位置',
'初始化': '随机数(防止重放攻击)'
},
'ack (确认号)': {
'作用': '期望收到的下一个字节',
'计算': '已经收到的 seq + 已接收数据长度'
},
'ACK': {
'作用': '确认标志',
'值': '1 表示这是一个确认应答'
}
};
// 2. TCP 四次挥手(断开连接)
/*
客户端 服务器
| |
| ---- FIN=1, seq=u -----------------> | 客户端发送 FIN
| |
| <--- ACK=1, ack=u+1 ----------------- | 服务器返回 ACK(客户端停止发送)
| |
| ... 等待服务器处理剩余数据 ... |
| |
| <--- FIN=1, seq=w ------------------- | 服务器发送 FIN
| |
| ---- ACK=1, ack=w+1 -----------------> | 客户端返回 ACK
| |
| ... 等待 2MSL ... |
| |
| 双方关闭连接 |
*/
// 2.1 挥手状态变化
const tcpWave = {
'FIN_WAIT_1': '已发送 FIN,等待 ACK(客户端)',
'FIN_WAIT_2': '已收到 ACK,等待服务器 FIN',
'CLOSE_WAIT': '已收到 FIN,等待应用处理(服务器)',
'CLOSING': '双方同时发送 FIN,收到对方 ACK',
'LAST_ACK': '已收到对方 FIN,等待 ACK 确认',
'TIME_WAIT': '收到 FIN 并发送 ACK,等待 2MSL'
};
// 2.2 为什么需要四次?
/*
- 第一次:客户端 -> 服务器(客户端不再发送数据)
- 第二次:服务器 -> 客户端(确认收到,但可能还有数据要发送)
- 第三次:服务器 -> 服务器(服务器处理完数据,发送 FIN)
- 第四次:客户端 -> 服务器(确认收到)
中间两次可以合并,但主动关闭方需要等待对方处理完
*/
// 2.3 TIME_WAIT 等待 2MSL 的原因
const timeWaitReasons = {
'MSL': 'Maximum Segment Lifetime,报文最大生存时间(约 60 秒)',
'原因1': '确保对方收到 ACK(如果 ACK 丢失,对方会重发 FIN)',
'原因2': '让本连接的所有报文在网络中消失(避免影响新连接)'
};
// 3. TCP 状态转换图
const tcpStateDiagram = `
CLOSED -> SYN_SENT (发送 SYN)
CLOSED -> LISTEN (被动打开)
LISTEN -> SYN_RECEIVED (收到 SYN)
SYN_SENT -> ESTABLISHED (收到 SYN+ACK)
SYN_RECEIVED -> ESTABLISHED (收到 ACK)
ESTABLISHED -> FIN_WAIT_1 (主动关闭)
ESTABLISHED -> CLOSE_WAIT (收到 FIN)
FIN_WAIT_1 -> FIN_WAIT_2 (收到 ACK)
FIN_WAIT_2 -> CLOSING (收到 FIN)
FIN_WAIT_1 -> TIME_WAIT (收到 FIN+ACK)
CLOSING -> TIME_WAIT (收到 ACK)
CLOSE_WAIT -> LAST_ACK (发送 FIN)
LAST_ACK -> CLOSED (收到 ACK)
TIME_WAIT -> CLOSED (2MSL 超时)
`;
// 4. TCP 可靠传输机制
const tcpReliable = {
'确认应答 (ACK)': '收到数据后发送 ACK 确认',
'超时重传': '发送后等待 ACK,超时则重发',
'序列号': '每个字节都有唯一序列号',
'流量控制': '滑动窗口避免发送过快',
'拥塞控制': '慢启动、拥塞避免、快速恢复'
};
// 5. TCP 滑动窗口
function slidingWindow() {
// 窗口大小:无需等待 ACK 就能发送的数据量
// 窗口值由接收方在 ACK 中告知
// 发送方窗口
/*
| 已发送已确认 | 已发送未确认 | 可发送未发送 | 不可发送 |
[0 -----------|---发送窗口---|------------- capacity]
*/
// 示例
const sendWindow = {
'已发送已确认': 0, // 这部分数据已被确认
'已发送未确认': 1000, // 这部分数据已发送,等待 ACK
'可用窗口': 2000, // 可以发送的新数据
'总窗口大小': 3000 // 滑动窗口容量
};
// 接收方窗口
/*
| 已接收已确认 | 期望接收 | 不可接收 |
*/
// 窗口满时,发送方停止发送,等待 ACK
}
// 6. TCP 拥塞控制
const tcpCongestion = {
'慢启动 (Slow Start)': {
'开始': 'cwnd = 1 MSS',
'增长': '每收到一个 ACK,cwnd += 1 MSS',
'终止': '达到慢启动阈值 ssthresh 或出现丢包'
},
'拥塞避免': {
'开始': 'cwnd 达到 ssthresh',
'增长': '每收到一个 ACK,cwnd += 1/cwnd(线性增长)'
},
'快速重传': {
'触发': '收到 3 个重复 ACK',
'动作': '立即重发丢失的 segment,不等超时'
},
'快速恢复': {
'动作': 'ssthresh = cwnd/2, cwnd = ssthresh + 3, 进入拥塞避免'
}
};
4.2 HTTP 缓存机制
javascript
复制代码
// 1. 强缓存(不需要发送请求到服务器)
// 命中强缓存时,浏览器直接使用本地资源,不发送请求
// 1.1 Cache-Control
// 优先级高于 Expires
const cacheControlDirectives = {
'max-age=3600': {
'含义': '资源在 3600 秒内新鲜',
'计算': '缓存有效期 = 缓存创建时间 + max-age',
'示例': 'Cache-Control: max-age=3600'
},
'no-cache': {
'含义': '不使用强缓存,每次都发请求到服务器(可能用协商缓存)',
'注意': '不等于 no-store'
},
'no-store': {
'含义': '完全不缓存,所有请求都直接到服务器',
'使用': '敏感数据(银行记录等)'
},
'private': {
'含义': '只能在浏览器缓存,不能在代理服务器缓存',
'使用': '用户个人信息等'
},
'public': {
'含义': '可以被任何节点缓存(浏览器、CDN、代理)',
'使用': '静态公共资源'
},
'must-revalidate': {
'含义': '缓存过期后必须验证有效性',
'使用': '避免使用过期缓存'
},
's-maxage=86400': {
'含义': '在代理服务器中的缓存时间(覆盖 max-age)',
'使用': 'CDN 缓存时间控制'
}
};
// 1.2 Expires(旧方式,不推荐)
// 缺陷:服务器时间和客户端时间可能不一致
const expiresExample = `
Expires: Mon, 12 May 2026 23:59:59 GMT
`;
// 2. 协商缓存(需要发送请求到服务器验证)
// 浏览器发送请求,服务器判断是否可以使用本地缓存
// 2.1 Last-Modified / If-Modified-Since
// 服务器返回 Last-Modified,浏览器下次请求时发送 If-Modified-Since
const lastModifiedFlow = `
请求:
GET /api/users HTTP/1.1
响应:
HTTP/1.1 200 OK
Last-Modified: Mon, 12 May 2026 10:00:00 GMT
Content-Type: application/json
{users: [...]}
---
再次请求:
GET /api/users HTTP/1.1
If-Modified-Since: Mon, 12 May 2026 10:00:00 GMT
如果资源未变化:
HTTP/1.1 304 Not Modified
(无响应体)
如果资源已变化:
HTTP/1.1 200 OK
Last-Modified: Mon, 12 May 2026 11:00:00 GMT
{users: [...]} // 新内容
`;
// 2.2 ETag / If-None-Match
// 服务器返回 ETag(资源内容的哈希),浏览器下次请求时发送 If-None-Match
const etagFlow = `
请求:
GET /api/users HTTP/1.1
响应:
HTTP/1.1 200 OK
ETag: "abc123"
Content-Type: application/json
{users: [...]}
---
再次请求:
GET /api/users HTTP/1.1
If-None-Match: "abc123"
如果资源未变化:
HTTP/1.1 304 Not Modified
(无响应体)
如果资源已变化:
HTTP/1.1 200 OK
ETag: "def456"
{users: [...]} // 新内容
`;
// 2.3 ETag vs Last-Modified
const cacheCompare = {
'Last-Modified': {
'优点': '简单,HTTP 协议内置',
'缺点': '时间精度到秒,短时间内变化可能检测不到'
},
'ETag': {
'优点': '精确检测(基于内容哈希),支持弱 ETag',
'缺点': '计算哈希有性能开销'
}
};
// 3. 完整缓存流程图
const cacheFlow = `
浏览器请求资源
|
v
检查强缓存(Cache-Control / Expires)
|
| 命中
v
直接使用本地缓存(200 from memory/disk cache)
|
| 未命中
v
发送请求到服务器,带上协商缓存头
If-None-Match / If-Modified-Since
|
v
检查协商缓存
|
| 命中(资源未变化)
v
304 Not Modified,使用本地缓存
|
| 未命中(资源已变化)
v
200 OK,返回新内容
更新本地缓存
`;
// 4. 缓存决策树
const cacheDecision = {
'请求到达': '检查是否有缓存',
'否': '向服务器请求',
'是': '检查缓存新鲜度',
'新鲜': '直接使用(强缓存命中)',
'不新鲜': '发起协商缓存请求',
'服务器确认未变': '304,使用缓存',
'服务器确认已变': '200,返回新内容'
};
// 5. 实际应用示例
const express = require('express');
const app = express();
// 5.1 设置强缓存
app.get('/static/*', (req, res) => {
// 静态资源缓存一年
res.set('Cache-Control', 'public, max-age=31536000');
// 或者只让浏览器缓存(CDN 不缓存)
// res.set('Cache-Control', 'private, max-age=31536000');
});
// 5.2 设置协商缓存
app.get('/api/data', (req, res) => {
const data = getData();
const lastModified = data.lastModified;
const etag = data.etag;
// 检查 If-None-Match
if (req.headers['if-none-match'] === etag) {
return res.status(304).end();
}
// 检查 If-Modified-Since
if (req.headers['if-modified-since'] === lastModified) {
return res.status(304).end();
}
// 返回完整内容
res.set({
'Last-Modified': lastModified,
'ETag': etag,
'Cache-Control': 'private, max-age=3600' // 协商缓存仍然需要设置缓存时间
});
res.json(data);
});
// 5.3 不缓存
app.get('/api/realtime', (req, res) => {
res.set('Cache-Control', 'no-store, no-cache, must-revalidate');
res.json(getRealtimeData());
});
// 6. 用户操作对缓存的影响
const userActions = {
'F5 / 刷新按钮': {
'行为': '跳过强缓存,检查协商缓存',
'原因': '用户想看到最新内容'
},
'Ctrl + F5': {
'行为': '跳过所有缓存,重新请求',
'结果': 'Cache-Control: no-cache'
},
'地址栏回车': {
'行为': '正常走缓存策略'
},
'新窗口打开链接': {
'行为': '检查是否有对应的缓存(根据 URL)'
},
'后退按钮': {
'行为': '使用缓存(跳过强缓存检查)',
'原因': '用户刚访问过,应该是最新或足够新'
}
};
五、WebSocket 与长连接
5.1 WebSocket 协议
javascript
复制代码
// 1. WebSocket vs HTTP
const wsVsHttp = {
'连接方式': {
'HTTP': '请求-响应模式,客户端发起,服务器被动响应',
'WebSocket': '全双工通信,双方都可以主动发送消息'
},
'建立连接': {
'HTTP': '客户端发请求,服务器返回响应,连接关闭',
'WebSocket': '通过 HTTP 升级(Upgrade)建立持久连接'
},
'数据格式': {
'HTTP': '文本或二进制,有请求头开销',
'WebSocket': '帧格式,最小 2 字节头,效率高'
},
'适用场景': {
'HTTP': 'REST API、文件上传下载、简单查询',
'WebSocket': '实时聊天、在线协作、游戏、推送通知'
}
};
// 2. WebSocket 握手
/*
客户端 -> 服务器:HTTP 请求(带升级头)
GET /ws HTTP/1.1
Host: api.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
服务器 -> 客户端:HTTP 响应(101 切换协议)
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYG3hQ76O/a3F=/a=
*/
// 3. WebSocket 帧结构
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-------------+-------------+-------------+---------------+
|F|R|R|R| Opcode |M| Payload length | Extended |
|I|S|S|S| (4) |A| (7) | payload |
|N|V|V|V| |S| | length |
| |1|2|3| |K| | (16/64) |
+-+-------------+-------------+-------------+---------------+
| Extended payload length continued, if payload len == 127
+---------------------------------------------------------------+
| | Masking-key, if MASK set to 1
+-------------------------------+-------------------------------+
| Masking-key (continued), if MASK = 1 |
+---------------------------------------------------------------+
: Payload Data :
+---------------------------------------------------------------+
*/
// 4. WebSocket opcode
const wsOpcodes = {
'0x0': 'Continuation frame(延续帧,用于分割大消息)',
'0x1': 'Text frame(文本帧)',
'0x2': 'Binary frame(二进制帧)',
'0x8': 'Close frame(关闭连接)',
'0x9': 'Ping frame(心跳检测)',
'0xA': 'Pong frame(响应心跳)'
};
// 5. WebSocket API 使用
// 浏览器端
class WebSocketClient {
constructor(url) {
// 1. 创建 WebSocket 连接
this.ws = new WebSocket('wss://api.example.com/ws');
// 2. 连接打开
this.ws.onopen = (event) => {
console.log('WebSocket 连接已建立');
// 发送消息
this.ws.send(JSON.stringify({ type: 'subscribe', channel: 'notifications' }));
};
// 3. 收到消息
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
};
// 4. 连接关闭
this.ws.onclose = (event) => {
console.log('WebSocket 连接关闭', event.code, event.reason);
// 可能需要重连
this.reconnect();
};
// 5. 错误处理
this.ws.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
}
// 发送消息
send(data) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
}
// 关闭连接
close(code = 1000, reason = '') {
this.ws.close(code, reason);
}
// 重连机制
reconnect() {
setTimeout(() => {
console.log('尝试重新连接...');
this.ws = new WebSocket('wss://api.example.com/ws');
}, 3000);
}
}
// 6. Node.js WebSocket 服务器
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8080 });
server.on('connection', (ws, req) => {
// 获取客户端 IP
const clientIP = req.socket.remoteAddress;
// 6.1 发送消息给客户端
ws.send('欢迎连接 WebSocket');
// 6.2 接收客户端消息
ws.on('message', (message) => {
console.log('收到消息:', message.toString());
// 解析消息
const data = JSON.parse(message.toString());
// 处理不同类型的消息
switch (data.type) {
case 'ping':
ws.send(JSON.stringify({ type: 'pong', timestamp: Date.now() }));
break;
case 'broadcast':
// 广播给所有客户端
server.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(data.message);
}
});
break;
}
});
// 6.3 心跳机制
// 定期发送 ping,客户端响应 pong
const pingInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.ping();
}
}, 30000);
// 6.4 关闭连接
ws.on('close', () => {
clearInterval(pingInterval);
console.log('客户端断开连接');
});
// 6.5 处理错误
ws.on('error', (error) => {
console.error('WebSocket 错误:', error);
});
});
// 7. WebSocket 心跳和断线检测
class HeartbeatWebSocket {
constructor(url, options = {}) {
this.url = url;
this.options = options;
this.reconnectInterval = options.reconnectInterval || 3000;
this.heartbeatInterval = options.heartbeatInterval || 30000;
this.ws = null;
this.timer = null;
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('连接建立');
this.startHeartbeat();
};
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'pong') {
console.log('心跳响应正常');
}
};
this.ws.onclose = () => {
console.log('连接断开');
this.stopHeartbeat();
this.reconnect();
};
this.ws.onerror = (error) => {
console.error('连接错误');
};
}
startHeartbeat() {
this.timer = setInterval(() => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: 'ping' }));
}
}, this.heartbeatInterval);
}
stopHeartbeat() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
reconnect() {
setTimeout(() => {
console.log('尝试重连...');
this.connect();
}, this.reconnectInterval);
}
}
六、CORS 跨域
6.1 CORS 原理与实现
javascript
复制代码
// 1. CORS (Cross-Origin Resource Sharing) 跨域资源共享
// 允许浏览器向不同源(协议、域名、端口)的服务器发送请求
// 2. 简单请求 vs 预检请求
// 2.1 简单请求条件(同时满足):
const simpleRequestConditions = [
'请求方法:GET、POST、HEAD',
'Content-Type:application/x-www-form-urlencoded、multipart/form-data、text/plain',
'无自定义请求头(如 Authorization)',
'请求中没有使用 ReadableStream 对象'
];
// 简单请求流程
const simpleCorsFlow = `
浏览器 -> 服务器:发送请求(含 Origin 头)
Origin: https://example.com
服务器 -> 浏览器:返回响应(含 Access-Control-Allow-Origin 头)
Access-Control-Allow-Origin: https://example.com
浏览器检查是否允许,如果不允许,抛出错误
`;
// 2.2 预检请求(复杂请求)
// 发送预检请求(OPTIONS)询问服务器是否允许
// 预检请求流程
const preflightCorsFlow = `
浏览器 -> 服务器:预检请求(OPTIONS)
Origin: https://example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
服务器 -> 浏览器:预检响应
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400 // 预检结果缓存 24 小时
浏览器 -> 服务器:实际请求(带 Origin)
服务器 -> 浏览器:实际响应(含 Access-Control-Allow-Origin)
`;
// 3. CORS 响应头
const corsResponseHeaders = {
'Access-Control-Allow-Origin': {
'示例': 'https://example.com 或 *',
'说明': '允许的来源,* 表示所有来源(不建议)'
},
'Access-Control-Allow-Methods': {
'示例': 'GET, POST, PUT, DELETE, OPTIONS',
'说明': '允许的 HTTP 方法'
},
'Access-Control-Allow-Headers': {
'示例': 'Content-Type, Authorization, X-Requested-With',
'说明': '允许的请求头'
},
'Access-Control-Allow-Credentials': {
'示例': 'true',
'说明': '是否允许携带 Cookie(需配合前端 withCredentials)'
},
'Access-Control-Max-Age': {
'示例': '86400',
'说明': '预检结果缓存时间(秒)'
},
'Access-Control-Expose-Headers': {
'示例': 'X-Total-Count, X-Page-Number',
'说明': '允许浏览器访问的响应头'
}
};
// 4. Express CORS 中间件实现
const express = require('express');
// 4.1 简单 CORS 配置
const cors = require('cors');
// 使用 cors 中间件
app.use(cors());
// 4.2 限制来源的 CORS
app.use(cors({
// 只允许特定域名
origin: 'https://example.com',
// 或使用函数动态判断
origin: (req, callback) => {
const allowed = ['https://example.com', 'https://api.example.com'];
callback(null, allowed.includes(req.header('Origin')));
},
// 允许携带 Cookie
credentials: true,
// 预检缓存时间
maxAge: 86400
}));
// 4.3 手动设置 CORS 头
app.use((req, res, next) => {
// 获取 Origin
const origin = req.headers.origin;
// 检查是否允许该来源
const allowedOrigins = ['https://example.com', 'https://api.example.com'];
if (allowedOrigins.includes(origin)) {
res.set({
'Access-Control-Allow-Origin': origin,
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Allow-Credentials': 'true'
});
}
// 处理预检请求
if (req.method === 'OPTIONS') {
return res.status(204).end();
}
next();
});
// 5. 前端 fetch 配置
// 5.1 简单请求
fetch('/api/users')
.then(res => res.json())
.then(data => console.log(data));
// 5.2 带 Cookie 的请求
fetch('/api/users', {
credentials: 'include' // 携带 Cookie
})
.then(res => {
// 服务器需要设置 Access-Control-Allow-Credentials: true
// 且 Access-Control-Allow-Origin 不能为 *
return res.json();
});
// 5.3 带自定义头的请求(触发预检)
fetch('/api/users', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123' // 自定义头
},
body: JSON.stringify({ name: 'Alice' })
});
// 6. 常见 CORS 问题与解决
// 6.1 蝴蝶效应
// 简单请求不能有自定义头,否则变预检请求
// 预检请求需要服务器正确处理 OPTIONS
// 6.2 携带 Cookie 的坑
// 服务器 Access-Control-Allow-Origin 不能是 *
// 必须指定具体域名
// 6.3 预检缓存
// 设置 Access-Control-Max-Age 减少 OPTIONS 请求
// 6.4 代理服务器
// 开发环境使用代理,避免 CORS 问题
// Vite 配置
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true
}
}
}
};
// 7. CORS vs JSONP
const corsVsJsonp = {
'CORS': {
'支持方法': 'GET、POST、PUT、DELETE 等所有方法',
'支持头': '支持自定义请求头',
'安全性': '支持 Cookie、Authorization',
'错误处理': '有完整的错误信息',
'兼容性': 'IE10+'
},
'JSONP': {
'支持方法': '只支持 GET',
'支持头': '不支持自定义请求头',
'安全性': '不安全(动态脚本注入)',
'错误处理': '难以捕获错误',
'兼容性': '支持老浏览器(如 IE6、IE7)'
}
};
6.2 跨域解决方案
javascript
复制代码
// 1. 跨域通信方案对比
const crossDomainSolutions = {
'CORS': {
'原理': '服务器设置允许跨域的响应头',
'前端': '正常发请求,无需额外处理',
'后端': '设置 Access-Control-Allow-Origin 等头',
'适用': 'REST API、XMLHttpRequest/fetch'
},
'JSONP': {
'原理': '利用 script 标签不受同源限制',
'前端': '回调函数 + script 标签',
'后端': '返回函数调用字符串',
'适用': 'GET 请求,老浏览器',
'缺点': '只支持 GET,不安全'
},
'代理服务器': {
'原理': '同源代理请求到目标服务器',
'前端': '请求同源代理',
'后端': '代理服务器转发请求',
'适用': '开发环境、服务器配置有限'
},
'WebSocket': {
'原理': 'WebSocket 不受同源限制',
'前端': 'new WebSocket(wsUrl)',
'后端': '正常处理 WebSocket 连接',
'适用': '实时通信'
},
'postMessage': {
'原理': 'window.postMessage 跨窗口通信',
'前端': '发送:iframe.contentWindow.postMessage',
'后端': '监听:window.addEventListener(message)',
'适用': '页面嵌套 iframe 通信'
},
'window.name': {
'原理': 'window.name 在页面跳转时保持',
'前端': '设置 name,通过 iframe 跳转传递',
'后端': '无需修改',
'适用': '特定场景(历史遗留)'
}
};
// 2. JSONP 实现
function jsonp(url, params = {}, callbackName = 'callback') {
return new Promise((resolve, reject) => {
// 2.1 生成唯一回调函数名
const callbackId = `jsonp_${Date.now()}_${Math.random().toString(36).substr(2)}`;
// 2.2 构建 URL 参数
const queryString = Object.entries({ ...params, [callbackName]: callbackId })
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
.join('&');
const fullUrl = `${url}${url.includes('?') ? '&' : '?'}${queryString}`;
// 2.3 创建 script 标签
const script = document.createElement('script');
script.src = fullUrl;
// 2.4 定义回调函数
window[callbackId] = (data) => {
delete window[callbackId];
document.body.removeChild(script);
resolve(data);
};
// 2.5 错误处理
script.onerror = () => {
delete window[callbackId];
document.body.removeChild(script);
reject(new Error('JSONP request failed'));
};
// 2.6 发送请求
document.body.appendChild(script);
});
}
// 3. postMessage 实现
// 3.1 发送消息
function sendMessage(targetWindow, message, targetOrigin) {
// targetWindow:目标窗口(iframe.contentWindow、popup)
// message:消息内容(可以是任何可序列化对象)
// targetOrigin:目标窗口的源(安全考虑)
targetWindow.postMessage(message, targetOrigin);
}
// 3.2 接收消息
window.addEventListener('message', (event) => {
// 3.3 验证来源(安全必须)
if (event.origin !== 'https://expected-origin.com') {
return;
}
// 3.4 处理消息
const { type, data } = event.data;
switch (type) {
case 'notification':
console.log('收到通知:', data);
break;
case 'response':
console.log('收到响应:', data);
break;
}
});
// 3.5 iframe 通信示例
// 父页面
const iframe = document.querySelector('iframe');
iframe.contentWindow.postMessage({ type: 'ping' }, 'https://child-origin.com');
// 子页面
window.addEventListener('message', (event) => {
if (event.origin !== 'https://parent-origin.com') return;
if (event.data.type === 'ping') {
event.source.postMessage({ type: 'pong' }, event.origin);
}
});
// 4. 反向代理配置
// 4.1 Nginx 反向代理
/*
server {
listen 80;
server_name example.com;
location /api/ {
# 代理到后端服务
proxy_pass http://backend-server:3000/;
# 设置代理请求头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 允许跨域
add_header Access-Control-Allow-Origin $http_origin always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
}
}
*/
// 4.2 Vite 开发代理
// vite.config.js
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
// 处理 OPTIONS 请求
configure: (proxy) => {
proxy.on('proxyReq', (proxyReq, req) => {
if (req.method === 'OPTIONS') {
proxyReq.setHeader('Access-Control-Allow-Origin', req.headers.origin);
proxyReq.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
proxyReq.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
}
});
}
}
}
}
});
七、网络安全
7.1 XSS 与 CSRF
javascript
复制代码
// 1. XSS(Cross-Site Scripting)跨站脚本攻击
// 攻击者在目标网站注入恶意脚本
// 1.1 存储型 XSS
// 恶意脚本存储在服务器数据库中,用户访问时执行
/*
攻击流程:
1. 攻击者在论坛评论中提交:<script>stealCookies()</script>
2. 服务器存储该内容到数据库
3. 其他用户访问该帖子
4. 浏览器执行恶意脚本,窃取 Cookie
*/
// 1.2 反射型 XSS
// 恶意脚本通过 URL 参数传递给服务器,服务器未过滤直接返回
/*
攻击流程:
1. 攻击者构造 URL:https://example.com/search?q=<script>alert()</script>
2. 用户点击该链接
3. 服务器获取参数 q,未过滤
4. 响应包含:<script>alert()</script>
5. 浏览器执行恶意脚本
*/
// 1.3 DOM 型 XSS
// 恶意脚本通过修改 DOM 执行,不需要服务器参与
/*
攻击流程:
1. 页面中有:document.write(location.search)
2. 攻击者构造 URL:?q=<img src=x onerror=alert()>
3. 浏览器执行:document.write(<img src=x onerror=alert()>)
*/
// 2. XSS 防御
// 2.1 输入过滤
function sanitizeInput(input) {
// 移除危险字符
return input
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g, '/');
}
// 2.2 输出编码
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 2.3 CSP(Content Security Policy)
// 限制页面可以执行的脚本来源
// 服务器响应头
// Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://trusted-cdn.com
// 2.4 HttpOnly Cookie
// Cookie 设置 HttpOnly 后,JavaScript 无法读取
// Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict
// 2.5 验证码/Token
// 防止自动化攻击
// 3. CSRF(Cross-Site Request Forgery)跨站请求伪造
// 诱导用户在其他网站向目标网站发起请求
// 3.1 CSRF 攻击流程
/*
1. 用户登录 target.com,登录状态存储在 Cookie
2. 用户访问 evil.com,页面中有:
<form action="https://target.com/delete-account" method="POST">
<input name="confirm" value="true">
</form>
<script>document.forms[0].submit()</script>
3. 浏览器自动携带 target.com 的 Cookie
4. target.com 接收到请求以为是用户操作
*/
// 3.2 CSRF 防御
// 3.2.1 CSRF Token
// 在表单中添加随机 Token,服务器验证
function csrfTokenExample() {
// 服务器端:生成 Token
function generateToken() {
return crypto.randomBytes(32).toString('hex');
}
// 服务器端:验证 Token
function verifyToken(req, res, next) {
const token = req.body._csrf || req.headers['x-csrf-token'];
const sessionToken = req.session.csrfToken;
if (token === sessionToken) {
next();
} else {
res.status(403).json({ error: 'Invalid CSRF token' });
}
}
// 前端:表单中添加 Token
// <input type="hidden" name="_csrf" value="<%= csrfToken %>">
}
// 3.2.2 SameSite Cookie
// 设置 Cookie 的 SameSite 属性
const sameSiteOptions = {
'Strict': {
'说明': '完全禁止跨域携带 Cookie',
'缺点': '用户体验差(跨站导航无法携带状态)'
},
'Lax': {
'说明': '允许顶级导航(地址栏变化)携带 Cookie',
'适用': '大多数场景',
'示例': '点击链接跳转可以携带,跳转iframe不可以'
},
'None': {
'说明': '允许跨域携带(需配合 Secure)',
'使用': '需要跨域 API 的场景',
'注意': '必须配合 Secure(HTTPS)'
}
};
// 3.2.3 验证来源(Origin/Referer)
function verifyOrigin(req) {
const origin = req.headers.origin;
const referer = req.headers.referer;
// 检查 Origin
if (origin && !allowedOrigins.includes(origin)) {
return false;
}
// 检查 Referer
if (referer && !allowedDomains.some(d => referer.startsWith(d))) {
return false;
}
return true;
}
// 4. 常见安全问题与防御
const securityChecklist = {
'XSS': {
'防御': ['输入过滤', '输出编码', 'CSP', 'HttpOnly'],
'工具': ['DOMPurify', 'sanitize-html']
},
'CSRF': {
'防御': ['CSRF Token', 'SameSite Cookie', '验证 Origin'],
'框架': ['csurf (Express)']
},
'SQL注入': {
'防御': ['参数化查询', 'ORM', '输入验证'],
'工具': ['SQLAlchemy', 'Hibernate']
},
'点击劫持': {
'防御': ['X-Frame-Options', 'CSP frame-ancestors'],
'示例': 'DENY / SAMEORIGIN'
},
'密码存储': {
'防御': ['bcrypt', 'argon2', '盐值'],
'禁止': ['明文存储', 'MD5/SHA1 单次哈希']
},
'HTTPS': {
'防御': ['强制 HTTPS', 'HSTS', '安全 Cookie'],
'配置': ['HTTPS only', 'Strict-Transport-Security']
}
};
// 5. 安全响应头
const securityHeaders = {
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'",
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'Access-Control-Allow-Origin': '具体域名,不是 *'
};
// Express 设置安全头
const helmet = require('helmet');
app.use(helmet()); // 自动设置多种安全头
7.2 HTTP/HTTPS 安全配置
javascript
复制代码
// 1. 安全响应头详解
// 1.1 CSP (Content Security Policy)
const cspConfig = {
'default-src': "'self'", // 默认只允许同源
'script-src': "'self' 'unsafe-inline' https://cdn.example.com",
'style-src': "'self' 'unsafe-inline'",
'img-src': "'self' data: https:",
'font-src': "'self' https://fonts.gstatic.com",
'connect-src': "'self' https://api.example.com",
'frame-src': "'none'",
'object-src': "'none'"
};
// 1.2 HSTS (HTTP Strict Transport Security)
const hstsConfig = {
'max-age': 31536000, // 强制 HTTPS 1 年
'includeSubDomains': true, // 包含子域名
'preload': true // 加入 HSTS 预加载列表
};
// 1.3 X-Frame-Options
const xFrameOptions = {
'DENY': '禁止被嵌入任何 iframe',
'SAMEORIGIN': '只允许同源 iframe',
'ALLOW-FROM uri': '允许特定来源(已废弃)'
};
// 1.4 X-Content-Type-Options
// 防止浏览器 MIME 类型嗅探
'X-Content-Type-Options: nosniff'
// 1.5 Referrer-Policy
const referrerPolicy = {
'no-referrer': '不发送 Referer',
'no-referrer-when-downgrade': 'HTTPS->HTTP 不发送(默认)',
'origin': '只发送源(协议+主机+端口)',
'origin-when-cross-origin': '跨域时只发送源',
'same-origin': '同源才发送',
'strict-origin-when-cross-origin': '严格模式',
'unsafe-url': '始终发送完整 URL(不安全)'
};
// 2. HTTPS 配置最佳实践
const httpsBestPractices = {
// 2.1 证书
'证书选择': {
'类型': '至少 DV,建议 OV 或 EV',
'算法': 'RSA 2048+ 或 ECDSA 256+',
'域名': '覆盖所有需要的域名和子域名'
},
// 2.2 TLS 配置
'TLS 版本': {
'最低': 'TLS 1.2',
'推荐': 'TLS 1.3',
'禁止': 'SSL 3.0, TLS 1.0, TLS 1.1'
},
// 2.3 加密套件
'cipher_suites': [
'TLS_AES_256_GCM_SHA384',
'TLS_AES_128_GCM_SHA256',
'TLS_CHACHA20_POLY1305_SHA256',
// 旧浏览器兼容(TLS 1.2)
'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384',
'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
],
// 2.4 其他配置
'配置项': {
'证书透明度': '启用 CT 日志',
'OCSP Stapling': '启用,提升握手性能',
'Session Tickets': '启用,支持会话恢复'
}
};
// 3. Node.js HTTPS 服务器配置
const https = require('https');
const fs = require('fs');
const options = {
// 证书
cert: fs.readFileSync('/path/to/cert.pem'),
key: fs.readFileSync('/path/to/key.pem'),
// TLS 版本控制
minVersion: 'TLSv1.2',
// 加密套件
ciphers: 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256',
// HSTS(通过响应头)
// Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
// 安全头(通过中间件设置)
// X-Frame-Options: DENY
// X-Content-Type-Options: nosniff
// X-XSS-Protection: 1; mode=block
};
const server = https.createServer(options, (req, res) => {
// 设置安全头
res.set({
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload',
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
'X-XSS-Protection': '1; mode=block',
'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
});
// 安全响应
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'Secure response' }));
});
server.listen(443, () => {
console.log('HTTPS server running on port 443');
});
八、网络性能优化
8.1 资源加载优化
javascript
复制代码
// 1. 关键资源预加载
// 提前加载即将使用的资源
// 1.1 预加载关键资源
/*
<link rel="preload" href="/fonts/custom.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="/main.js" as="script">
<link rel="preload" href="/styles.css" as="style">
*/
// 1.2 预连接(DNS + TCP + TLS)
/*
<link rel="preconnect" href="https://cdn.example.com">
<link rel="dns-prefetch" href="https://cdn.example.com">
*/
// 2. 懒加载实现
// 2.1 图片懒加载
function lazyLoadImages() {
// 使用 IntersectionObserver
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
// data-src 存储真实 src
img.src = img.dataset.src;
// 停止观察
observer.unobserve(img);
}
});
}, {
rootMargin: '50px 0px' // 提前 50px 开始加载
});
// 观察所有带 data-src 的图片
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
}
// 2.2 图片懒加载(原生支持)
/*
<img loading="lazy" src="/placeholder.jpg" data-src="/real-image.jpg">
*/
// 2.3 路由懒加载
// React
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
// Vue
// const Home = () => import('./pages/Home')
// 3. DNS 预解析
// <link rel="dns-prefetch" href="//api.example.com">
// 4. 资源合并与压缩
// 4.1 CSS/JS 合并
// Webpack 配置
const mergeConfig = {
// 使用 mini-css-extract-plugin 提取 CSS
// 使用 terser-webpack-plugin 压缩 JS
};
// 4.2 资源内联
// <script>
// // 小文件直接内联
// window.ENV = { API_URL: 'https://api.example.com' };
// </script>
// 5. 图片优化
const imageOptimization = {
'格式选择': {
'JPEG': '照片、复杂图像',
'PNG': '需要透明、图标',
'WebP': '现代浏览器,支持压缩比更高',
'AVIF': '最新格式,压缩比最高',
'SVG': '图标、简单图形'
},
'响应式图片': `
<img srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 480px, (max-width: 900px) 800px, 1200px"
src="medium.jpg" alt="描述">
`,
'CDN': '使用 CDN 加速图片加载'
};
8.2 HTTP/2 与 HTTP/3
javascript
复制代码
// 1. HTTP/2 主要特性
// 1.1 多路复用(Multiplexing)
// 在一个 TCP 连接中,并发发送多个请求和响应
// 解决了 HTTP/1.1 队头阻塞问题
// 对比:
// HTTP/1.1: 需要 6-8 个 TCP 连接并行请求
// HTTP/2: 只需要 1 个 TCP 连接
// 1.2 二进制分帧
// 数据以二进制帧传输,而不是文本
// 帧包含流 ID,用于多路复用
// 1.3 头部压缩(HPACK)
// 使用索引表压缩请求头
// 减少重复的头部传输
// 1.4 服务器推送
// 服务器可以主动推送资源给客户端
// 客户端请求 HTML,服务器主动推送 CSS/JS
// Node.js HTTP/2 服务器推送
const http2 = require('http2');
const fs = require('fs');
const server = http2.createSecureServer({
cert: fs.readFileSync('cert.pem'),
key: fs.readFileSync('key.pem')
});
server.on('stream', (stream, headers) => {
// 检查是否是主请求
if (headers[':path'] === '/') {
// 服务器推送 CSS
stream.pushStream({ ':path': '/styles.css' }, (err, pushStream) => {
pushStream.respond({
':status': 200,
'content-type': 'text/css'
});
pushStream.end('body { margin: 0; }');
});
// 返回 HTML
stream.respond({
':status': 200,
'content-type': 'text/html'
});
stream.end('<html><head><link rel="stylesheet" href="/styles.css"></head><body>Test</body></html>');
}
});
// 2. HTTP/3 (QUIC)
// 基于 UDP 的协议
// 2.1 HTTP/3 vs HTTP/2
const http3vs2 = {
'传输层': {
'HTTP/2': 'TCP + TLS 1.2/1.3',
'HTTP/3': 'UDP + QUIC(内置 TLS 1.3)'
},
'队头阻塞': {
'HTTP/2': 'TCP 层队头阻塞(丢包影响所有流)',
'HTTP/3': 'QUIC 层解决,无队头阻塞'
},
'连接建立': {
'HTTP/2': '1-3 个 RTT(TCP + TLS)',
'HTTP/3': '0-1 个 RTT(0-RTT、1-RTT)'
},
'迁移': {
'HTTP/2': 'IP 变化需要重建连接',
'HTTP/3': '连接 ID 支持无缝迁移'
}
};
// 3. HTTP/2 服务配置(Nginx)
/*
http2 on; # 启用 HTTP/2
# HTTP/2 服务器推送配置
location / {
root /var/www/html;
index index.html;
http2_push /styles.css;
http2_push /main.js;
}
# HTTP/2 相关优化
http2_max_concurrent_streams 256; # 单连接最大流数
http2_recv_buffer_size 256k; # 接收缓冲区
*/
// 4. 协议检测与降级
// 如果浏览器不支持 HTTP/2,自动降级到 HTTP/1.1
// 5. 前端资源策略调整
const http2ResourceStrategy = {
'不再需要域名分片': 'HTTP/2 多路复用,不需要多个域名',
'合并文件可能不再必要': 'HTTP/2 可以并发请求多个小文件',
'小文件内联': '减少请求数,但增加主文件大小',
'服务器推送利用': '让服务器主动推送关键资源'
};
九、网络面试高频问题
9.1 三次握手四次挥手
javascript
复制代码
// 面试问题:
// Q1: 为什么不两次握手?
// A: 两次握手无法确认双方的发送和接收能力都正常
// 第一次:客户端能发送,服务器能接收
// 第二次:服务器能发送,客户端能接收,客户端知道双方都正常
// Q2: 为什么四次挥手?
// A: TCP 是全双工,每个方向都需要单独关闭
// 客户端发送 FIN 表示不再发送,但还能接收
// 服务器 ACK 确认收到,但可能还有数据要发送
// 服务器发送 FIN 表示不再发送
// 客户端 ACK 确认,双方关闭
// Q3: TIME_WAIT 为什么是 2MSL?
// A: 确保对方收到 ACK(如果丢失会重发 FIN)
// 让旧连接的报文在网络中完全消失
// Q4: 三次握手可以变成四次吗?
// A: 可以,但没必要增加延迟
// 第二次握手可以合并 SYN + ACK
9.2 HTTPS 连接过程
javascript
复制代码
// 面试问题:
// Q1: HTTPS 为什么不直接用非对称加密?
// A: 非对称加密速度慢(100-1000 倍),不适合加密大量数据
// HTTPS 用非对称加密交换对称密钥,后续通信用对称加密
// Q2: HTTPS 中间人攻击能防止吗?
// A: 能,证书验证链、域名匹配、证书吊销检查
// Q3: 证书验证流程?
// A: 1. 检查证书是否由可信 CA 签发
// 2. 检查证书是否在有效期内
// 3. 检查证书域名是否匹配
// 4. 检查证书是否被吊销(CRL/OCSP)
9.3 HTTP 缓存
javascript
复制代码
// 面试问题:
// Q1: 强缓存和协商缓存区别?
// A: 强缓存不发送请求,协商缓存需要发送请求验证
// Q2: 什么情况返回 304?
// A: 协商缓存命中,资源未变化,返回 304 Not Modified
// Q3: 怎么让浏览器不缓存?
// A: Cache-Control: no-store, no-cache, must-revalidate
// 或者加时间戳参数:/api/users?t=timestamp
// Q4: 打开一个新页面用到的缓存?
// A: 地址栏回车:强缓存 -> 协商缓存
// F5:协商缓存
// Ctrl+F5:忽略所有缓存
9.4 WebSocket vs HTTP
javascript
复制代码
// 面试问题:
// Q1: 什么时候用 WebSocket?
// A: 实时通信(聊天、协作、游戏)、推送通知、频繁双向通信
// Q2: WebSocket 握手用 HTTP?
// A: 是,使用 HTTP Upgrade 机制升级为 WebSocket
// Q3: WebSocket 如何保持连接?
// A: 心跳机制,定期 ping/pong 检测对方是否存活
// Q4: WebSocket 断开重连策略?
// A: 指数退避:1s -> 2s -> 4s -> 8s -> 最大 30s
十、高频面试清单
网络必背清单
| 类别 |
题目 |
难度 |
重点 |
| HTTP |
请求/响应报文结构 |
⭐ |
各部分作用 |
| HTTP |
HTTP 方法区别 |
⭐ |
GET/POST/PUT/DELETE |
| HTTP |
状态码分类 |
⭐ |
2xx/3xx/4xx/5xx |
| HTTP |
持久连接 vs 管线化 |
⭐⭐ |
HTTP/1.1 特性 |
| HTTPS |
TLS 握手过程 |
⭐⭐ |
混合加密原理 |
| HTTPS |
为什么不只用非对称加密 |
⭐ |
性能问题 |
| DNS |
解析流程 |
⭐ |
递归查询、迭代查询 |
| DNS |
常见记录类型 |
⭐ |
A/CNAME/MX |
| TCP |
三次握手 |
⭐⭐ |
状态变化、为什么三次 |
| TCP |
四次挥手 |
⭐⭐ |
状态变化、为什么四次 |
| TCP |
TIME_WAIT |
⭐⭐ |
2MSL 原因 |
| 缓存 |
强缓存 vs 协商缓存 |
⭐⭐ |
Cache-Control/ETag |
| 缓存 |
304 何时返回 |
⭐ |
协商缓存命中 |
| 缓存 |
用户操作与缓存 |
⭐ |
F5/Ctrl+F5 |
| CORS |
简单请求 vs 预检 |
⭐⭐ |
条件判断 |
| CORS |
响应头含义 |
⭐ |
Access-Control-* |
| WebSocket |
握手过程 |
⭐⭐ |
HTTP Upgrade |
| WebSocket |
心跳机制 |
⭐ |
ping/pong |
| 安全 |
XSS 类型与防御 |
⭐⭐ |
CSP/输入输出编码 |
| 安全 |
CSRF 防御 |
⭐⭐ |
Token/SameSite |
| HTTP/2 |
多路复用原理 |
⭐⭐ |
流、帧、ID |
| HTTP/3 |
QUIC 优势 |
⭐⭐ |
UDP/0-RTT/无队头阻塞 |