HTTP 常考问题简洁回答
1. HTTP版本演进
-
HTTP/1.0:短连接,每个请求新建TCP连接
-
HTTP/1.1:长连接、管道化、Host头支持
-
HTTP/2 :二进制分帧(文本格式改成二进制格式传输数据)、多路复用(HTTP/2 实现了 Stream 并发, HTTP/1 队头阻塞)、头部压缩(避免重复性, HPACK 算法压缩)、服务器推送(服务器推送资源时,会先发送 PUSH_PROMISE 帧告诉客户端接下来在哪个 Stream 发送资源)
-
HTTP/3:基于QUIC(UDP)、0-RTT、解决队头阻塞
2. 三次握手
过程:SYN → SYN-ACK → ACK
先是服务端主动监听某个端口,处于 LISTEN 状态,客户端SYN 报文发送给服务端,表示向服务端发起连接,客户端处于 SYN-SENT 状态。服务端把 SYN 和 ACK 标志位置为 1,该报文发给客户端,之后服务端处于 SYN-RCVD 状态。客户端收到服务端报文后,ACK 标志位置为 1
为什么不是两次:
原因一:防止已失效的连接请求突然到达服务器,造成资源浪费和错误连接
在两次握手的情况下,服务端没有中间状态给客户端来阻止历史连接,导致服务端可能建立一个历史连接,造成资源浪费。
原因二:同步双方初始序列号
3. HTTP vs HTTPS
-
HTTP:明文传输,端口80
-
HTTPS:HTTP + SSL/TLS加密,端口443,需要证书
4. HTTP状态码
-
1xx:信息性状态码
-
2xx:成功(200 OK, 201 Created)
-
3xx:重定向(301 永久, 302 临时)
-
4xx:客户端错误(404 Not Found, 403 Forbidden)
-
5xx:服务器错误(500 Internal Server Error)
5. 网络协议栈
-
HTTP:应用层,请求-响应模型
-
TCP:传输层,可靠连接
-
UDP:传输层,无连接不可靠
-
Socket:操作系统网络编程接口
-
WebSocket:全双工通信协议,通过HTTP升级握手建立连接,避免HTTP的请求-响应开销,连接建立后保持打开状态
-
RPC:远程过程调用,像调用本地函数一样调用远程服务。HTTP 主要用于 B/S 架构,而 RPC 更多用于 C/S 架构。
-
Boost.Asio:C++跨平台网络编程库,支持同步/异步I/O
总结对比
| 技术 | 层次 | 主要特点 | 适用场景 |
|---|---|---|---|
| HTTP | 应用层 | 请求-响应、无状态 | Web服务、REST API |
| TCP | 传输层 | 可靠、面向连接 | 文件传输、数据库 |
| UDP | 传输层 | 快速、无连接 | 实时媒体、游戏 |
| Socket | 接口层 | 操作系统API | 底层网络编程 |
| WebSocket | 应用层 | 全双工、实时 | 聊天、实时数据 |
| RPC | 应用层 | 透明远程调用 | 微服务、分布式 |
| Boost.Asio | 库 | 异步、跨平台 | 高性能服务器 |
6. TCP vs UDP
| 特性 | TCP | UDP |
|---|---|---|
| 连接性 | 面向连接,一对一 | 无连接,一对多 |
| 可靠性 | 可靠传输 | 不可靠 |
| 顺序性 | 保证顺序 | 不保证 |
| 速度 | 慢 | 快 |
| 头部大小 | 大(20字节) | 小(8字节) |
7. TCP核心技术
-
重传机制 :超时重传(设定一个定时器,当超过指定的时间后,没有收到对方的
ACK确认应答报文,就会重发该数据。数据包丢失,确认应答丢失)、快速重传 -
拥塞控制:慢启动、拥塞避免、快速恢复。TCP 拥塞控制包含四个主要算法:慢启动、拥塞避免、快速重传、快速恢复。
-
滑动窗口:流量控制,动态调整发送窗口大小。滑动窗口是 TCP 实现流量控制和可靠传输的核心机制。它允许发送方在收到确认之前连续发送多个数据包。
发送窗口示意图:
+---+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |
+---+---+---+---+---+---+---+---+---+---+
↑ ↑ ↑ ↑
│ │ │ │
已确认 发送窗口起始 发送窗口结束 不可发送
- **流量控制:**流量控制防止发送方发送数据过快,导致接收方缓冲区溢出。接收方通过 TCP 头部的窗口大小字段告知发送方其剩余缓冲区大小
| 机制 | 目的 | 控制对象 | 实现方式 |
|---|---|---|---|
| 滑动窗口 | 提高传输效率 | 发送速率 | 允许连续发送多个数据包 |
| 流量控制 | 防止接收方溢出 | 发送速率 | 接收方通告窗口大小(rwnd) |
| 拥塞控制 | 防止网络拥塞 | 网络负载 | 动态调整拥塞窗口(cwnd) |
滑动窗口
你的理解:滑动窗口是用来控制发送速率的,使得客户端可以连续发送多个数据包。
完善后的描述 :
滑动窗口是 TCP 的可靠性保证和流量控制机制,它允许发送方在收到确认前连续发送多个数据包。通过维护一个"发送窗口",TCP 可以:
-
提高网络利用率(避免等待单个ACK)
-
保证数据按顺序到达
-
跟踪已发送但未确认的数据
核心是:在可靠传输的基础上提高效率。
流量控制
你的理解:流量控制是服务端告知客户端他可以接收多大的数据,来防止接收溢出。
完善后的描述 :
流量控制是接收方主导的速率限制机制 ,目的是防止发送方发送过快导致接收方缓冲区溢出。接收方通过 TCP 头部的窗口大小字段动态告知发送方其剩余的缓冲区空间。
关键点:
-
接收方控制:
接收窗口(rwnd) = 缓冲区总大小 - 已使用大小 -
零窗口问题:当接收窗口为0时,发送方暂停发送
-
窗口探测:发送方定期探测窗口是否重新打开
拥塞控制
你的理解:拥塞控制是发送端可以感知网络拥塞程度,通过几种算法来改变拥塞窗口大小,从而降低发送的数据量。
完善后的描述 :
拥塞控制是发送方根据网络状况动态调整发送速率的机制,目的是避免网络过载。通过四种核心算法:
-
慢启动:连接开始时指数增长,快速探测网络容量
-
拥塞避免:达到阈值后线性增长,谨慎增加负载
-
快速重传:收到3个重复ACK时立即重传,不等待超时
-
快速恢复:重传后适度降低速率,避免回到慢启动
拥塞感知方式:
-
显式信号:重复ACK(轻度拥塞)
-
隐式信号:超时重传(严重拥塞)
完整的关系描述
这三个机制共同工作,形成完整的 TCP 发送控制:
text
实际发送窗口 = min(拥塞窗口, 接收窗口)
发送数据量 ≤ 实际发送窗口
工作流程:
-
滑动窗口确保可靠传输和基础效率
-
流量控制防止接收方被淹没
-
拥塞控制防止网络被淹没
生动的比喻
可以把这三个机制比作高速公路系统:
-
滑动窗口:允许连续发车,但每辆车都要确认到达
-
流量控制:目的地停车场容量有限,通知出发站调整发车频率
-
拥塞控制:交警根据道路拥堵情况,动态调整允许上路的车辆数
总结
你的理解方向完全正确,主要需要完善的是:
-
滑动窗口 更强调可靠传输而不仅仅是速率控制
-
流量控制 明确是接收方主导的
-
拥塞控制 区分了不同拥塞程度的应对策略
8. UDP实现TCP
在应用层实现:
-
添加序列号保证顺序
-
确认机制和重传
-
流量控制和拥塞控制
-
如QUIC协议就是基于UDP的可靠传输
9. 浏览器输入URL过程
-
DNS解析域名 → IP
-
建立TCP连接(三次握手)
-
发送HTTP请求
-
服务器处理并返回响应
-
浏览器解析渲染(HTML→DOM→CSSOM→渲染树→布局→绘制)
-
加载资源(CSS、JS、图片等)
-
连接结束
补充常考问题
10. GET vs POST
-
GET:获取数据,参数在URL,有长度限制,可缓存
-
POST:提交数据,参数在请求体,无长度限制,不可缓存
11. Cookie vs Session
-
Cookie:客户端存储,大小有限(4KB),不安全
-
Session:服务端存储,安全,占用服务器资源
12. WebSocket握手
基于HTTP Upgrade头,从HTTP升级为WebSocket协议
13. HTTPS握手过程
-
客户端Hello(加密套件、随机数)
-
服务器Hello(证书、随机数)
-
客户端验证证书,生成预主密钥
-
交换密钥,开始加密通信
14. DNS解析过程
浏览器缓存 → 系统缓存 → 路由器缓存 → ISP DNS → 递归查询
15. CDN原理
将内容缓存到边缘节点,用户访问最近的节点,减少延迟
16. 跨域问题(CORS)
浏览器的同源策略限制,通过设置响应头Access-Control-Allow-Origin解决
17. 长连接 vs 短连接
-
短连接:每次请求新建连接,HTTP/1.0默认
-
长连接:复用TCP连接,HTTP/1.1默认
18. HTTP缓存机制
-
强缓存:Cache-Control, Expires
-
协商缓存:Last-Modified, ETag
19. RESTful API设计
-
使用HTTP方法表达操作(GET/POST/PUT/DELETE)
-
无状态、统一接口、资源导向
-
restful本质上是一种设计风格,没有特别的规定,主要是为了创建高效,安全,易于使用和维护的API
-
**RESTful API 是一种基于 HTTP 协议的设计风格,把网络上的所有事物都看作"资源",通过统一的接口对资源进行操作。**用 HTTP 方法代替动词,接口自描述。每次请求都包含所有必要信息,服务器不保存客户端状态。
-
RESTful API 就是用 HTTP 方法对 URL 标识的资源进行 CRUD 操作的一种标准化设计风格。
20. 负载均衡策略
-
轮询、加权轮询
-
最少连接、IP哈希
-
一致性哈希
Nginx有哪些负载均衡算法 轮询 IP哈希 URL哈希 最短响应时间 加权轮询
21.tcp粘包以及如何处理粘包
- 固定长度的消息;比如规定一个消息的长度是 64 个字节,当接收方接满 64 个字节,就认为这个内容是一个完整且有效的消息
- 特殊字符作为边界; HTTP 通过设置回车符、换行符作为 HTTP 报文协议的边界。(注意转义)
- 自定义消息结构。自定义一个消息结构,由包头和数据组成,其中包头包是固定大小的,而且包头里有一个字段来说明紧随其后的数据有多大。
TCP 粘包是指发送方发送的多个数据包被接收方当作一个数据包接收的现象。
TCP 是面向字节流的协议,没有消息边界的概念
22.https可能会考到的
HTTP 常考问题详细解答版
1. HTTP版本演进
HTTP/1.0(1996年)
核心特性:
-
短连接:每次请求都需要建立新的TCP连接,请求完成后立即关闭
-
无状态:服务器不记录之前请求的信息
-
基础功能:支持GET、POST、HEAD方法
问题:
html
<!-- 加载一个网页需要多个连接 -->
建立连接 → 请求HTML → 关闭连接
建立连接 → 请求CSS → 关闭连接
建立连接 → 请求JS → 关闭连接
建立连接 → 请求图片 → 关闭连接
性能瓶颈:频繁的TCP连接建立和关闭消耗大量资源
HTTP/1.1(1999年)
核心改进:
-
持久连接:默认保持TCP连接,可发送多个请求
-
管道化:允许同时发送多个请求,但必须按顺序接收响应
-
分块传输:支持流式传输大文件
-
Host头:支持虚拟主机,一个IP托管多个网站
-
缓存控制:引入Cache-Control、ETag等
示例:
http
GET /page1 HTTP/1.1
Host: example.com
Connection: keep-alive
GET /page2 HTTP/1.1
Host: example.com
Connection: keep-alive
仍存在的问题:
-
队头阻塞:前一个请求延迟会影响后续请求
-
头部冗余:每次请求都发送完整的header
HTTP/2(2015年)
革命性改进:
二进制分帧:
cpp
// 不再是文本协议,而是二进制帧
+--------------------------------+
| 长度(24位) | 类型(8位) | 标志(8位) |
| 流标识符(31位) | 载荷数据... |
+--------------------------------+
多路复用:
text
客户端: 请求A帧1 → 请求B帧1 → 请求A帧2 → 请求B帧2
服务器: 响应A帧1 → 响应B帧1 → 响应A帧2 → 响应B帧2
完全解决了队头阻塞问题!
头部压缩:
-
使用HPACK算法压缩header
-
维护静态表(61个常用header)和动态表
-
相同header只需发送索引号
服务器推送:
http
客户端请求: GET /index.html
服务器响应:
- 推送 /style.css
- 推送 /script.js
- 然后发送 index.html
HTTP/3(2020年)
基于QUIC协议:
核心优势:
-
基于UDP:避免TCP队头阻塞
-
0-RTT连接:首次连接1-RTT,后续连接0-RTT
-
改进的拥塞控制:更好的网络适应性
-
连接迁移:IP地址变化时连接不中断
QUIC内部结构:
text
QUIC数据包 = [头部] + [帧1: STREAM数据] + [帧2: ACK] + [帧3: 加密握手]
2. 三次握手详细解析
握手过程
cpp
客户端 → 服务器: SYN=1, seq=x // 我想连接,我的初始序号是x
客户端 ← 服务器: SYN=1, ACK=1, seq=y, ack=x+1 // 我同意连接,我的序号是y,确认你的x
客户端 → 服务器: ACK=1, seq=x+1, ack=y+1 // 好的,连接建立
为什么不是两次?
关键问题:防止已失效的连接请求
场景分析:
text
情况1:正常通信
客户端 → 服务器: SYN (正常到达)
客户端 ← 服务器: SYN-ACK
客户端 → 服务器: ACK ✓ 连接建立
情况2:网络延迟导致的问题
客户端 → 服务器: SYN (在网络中滞留)
客户端 ← 服务器: SYN-ACK
客户端 → 服务器: ACK ✓
// 一段时间后,滞留的SYN突然到达服务器
客户端 → 服务器: SYN (滞留的旧包到达)
服务器 ← 服务器: SYN-ACK (认为新连接)
// 如果没有第三次握手,服务器就会建立无用连接!
两次握手的问题:
-
服务器无法区分新旧连接请求
-
可能建立大量无效连接,浪费资源
-
无法同步双方的初始序列号
3. HTTP vs HTTPS 深度对比
HTTP的问题
http
GET /login HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
username=alice&password=123456
风险:所有数据明文传输,可能被中间人窃取或篡改
HTTPS加密流程
text
HTTP层: [原始数据]
↓
TLS层: [加密数据] + [MAC认证]
↓
TCP层: 传输加密后的数据
SSL/TLS握手详细过程
text
1. ClientHello
- 支持的TLS版本
- 加密套件列表
- 客户端随机数
2. ServerHello
- 选择的TLS版本
- 选择的加密套件
- 服务器随机数
- 服务器证书
3. 证书验证
- 客户端验证证书有效性
- 检查证书链、有效期、吊销状态
4. 密钥交换
- 客户端生成预主密钥,用服务器公钥加密
- 双方用随机数+预主密钥生成会话密钥
5. 加密通信
- 使用会话密钥对称加密数据
- 用MAC保证数据完整性
数字证书验证
cpp
// 证书链验证过程
用户证书 → 中间CA证书 → 根CA证书
证书包含:
-
域名信息
-
公钥
-
颁发者信息
-
有效期
-
数字签名
4. HTTP状态码详解
1xx 信息性状态码
-
100 Continue:客户端应继续发送请求体
-
101 Switching Protocols:协议切换(如HTTP→WebSocket)
2xx 成功状态码
-
200 OK:请求成功
-
201 Created:资源创建成功(常用于POST)
-
202 Accepted:请求已接受,但处理未完成
-
204 No Content:成功但无返回内容
3xx 重定向状态码
cpp
// 永久重定向 - 浏览器会缓存
301 Moved Permanently: https://example.com
// 临时重定向 - 浏览器不缓存
302 Found: 临时重定向,可能改变请求方法
303 See Other: 必须用GET请求新URL
307 Temporary Redirect: 保持原请求方法
// 特殊重定向
304 Not Modified: 资源未修改,使用缓存
4xx 客户端错误
cpp
400 Bad Request: 请求语法错误
401 Unauthorized: 需要身份验证
403 Forbidden: 服务器拒绝请求
404 Not Found: 资源不存在
405 Method Not Allowed: 请求方法不被允许
408 Request Timeout: 请求超时
429 Too Many Requests: 请求过于频繁
5xx 服务器错误
cpp
500 Internal Server Error: 服务器内部错误
501 Not Implemented: 服务器不支持请求功能
502 Bad Gateway: 网关错误
503 Service Unavailable: 服务不可用
504 Gateway Timeout: 网关超时
5. 网络协议栈深度解析
TCP(传输控制协议)
特点:
-
面向连接、可靠传输
-
流量控制、拥塞控制
-
保证数据顺序
应用场景:
-
文件传输、邮件、网页浏览
-
需要可靠传输的场景
UDP(用户数据报协议)
特点:
-
无连接、尽最大努力交付
-
头部开销小、延迟低
-
不保证顺序和可靠性
应用场景:
-
视频流、语音通话、DNS查询
-
实时性要求高的场景
Socket编程模型
cpp
// TCP Server示例
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(server_fd, (struct sockaddr*)&address, sizeof(address));
listen(server_fd, 3);
int new_socket = accept(server_fd, (struct sockaddr*)&address, &addrlen);
// UDP Server示例
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
recvfrom(sockfd, buffer, sizeof(buffer), 0,
(struct sockaddr*)&cliaddr, &len);
WebSocket协议
握手过程:
http
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
数据帧格式:
text
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 len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
RPC(远程过程调用)
核心组件:
cpp
// 1. 接口定义
service UserService {
rpc GetUser(UserRequest) returns (UserResponse);
}
// 2. 序列化/反序列化
class UserRequest {
int user_id;
string token;
// 序列化为二进制
}
// 3. 网络传输
// 4. 服务发现和负载均衡
流行框架:gRPC、Thrift、Dubbo
Boost.Asio
异步编程模型:
cpp
#include <boost/asio.hpp>
void async_connect_example() {
boost::asio::io_context io;
boost::asio::ip::tcp::socket socket(io);
// 异步连接
socket.async_connect(
boost::asio::ip::tcp::endpoint(
boost::asio::ip::make_address("192.168.1.1"), 8080),
[](boost::system::error_code ec) {
if (!ec) {
std::cout << "连接成功" << std::endl;
}
}
);
io.run(); // 事件循环
}
优势:
-
跨平台支持
-
高性能异步I/O
-
支持TCP、UDP、SSL等协议
6. TCP vs UDP 深度对比
TCP特性详解
可靠性保证:
-
序列号和确认机制:每个字节都有序列号
-
超时重传:RTT动态计算超时时间
-
流量控制:滑动窗口机制
-
拥塞控制:慢启动、拥塞避免等算法
头部结构:
text
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 源端口号 | 目标端口号 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 序列号 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 确认序列号 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 数据偏移 | 保留 |U|A|P|R|S|F| 窗口大小 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 校验和 | 紧急指针 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
UDP特性详解
简单高效:
-
无连接建立开销
-
头部固定8字节
-
适合广播和多播
头部结构:
text
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 源端口号 | 目标端口号 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 长度 | 校验和 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
选择标准
cpp
// 选择TCP的情况
if (需要可靠传输 || 需要保证顺序 || 传输大量数据) {
使用TCP;
}
// 选择UDP的情况
if (实时性要求高 || 能容忍数据丢失 || 小数据包频繁发送) {
使用UDP;
}
7. TCP核心技术深度解析
重传机制
超时重传:
cpp
// 动态计算超时时间
Timeout = SRTT + 4 * RTTVAR
其中:
SRTT = α × 旧SRTT + (1-α) × 新RTT // 平滑RTT
RTTVAR = β × 旧RTTVAR + (1-β) × |SRTT - 新RTT|
快速重传:
text
发送方: 发送包1,2,3,4,5,6
接收方: 确认1 → 确认2 → 重复确认2(包3丢失) → 重复确认2 → 重复确认2
发送方: 收到3个重复确认,立即重传包3
拥塞控制
四个阶段:
-
慢启动:
cpp
cwnd = 1 MSS // 初始拥塞窗口 每收到一个ACK: cwnd = cwnd + 1 MSS // 指数增长,直到达到慢启动阈值 -
拥塞避免:
cpp
// 线性增长 每RTT时间: cwnd = cwnd + 1 MSS -
快速重传/快速恢复:
cpp
// 收到3个重复ACK时 ssthresh = cwnd / 2 cwnd = ssthresh + 3 MSS // 每收到重复ACK: cwnd = cwnd + 1 MSS // 收到新数据ACK: cwnd = ssthresh
滑动窗口
工作原理:
text
发送窗口: [已发送已确认 | 已发送未确认 | 可发送 | 不可发送]
接收窗口: [已接收已确认 | 可接收 | 不可接收]
流量控制:
cpp
// 接收方通过窗口大小控制发送速率
接收窗口 = 接收缓冲区大小 - 已接收但未处理数据大小
发送窗口 = min(拥塞窗口, 接收窗口)
8. 基于UDP实现可靠传输
实现框架
cpp
class ReliableUDP {
private:
// 序列号管理
uint32_t next_seq;
std::map<uint32_t, Packet> unacked_packets;
// 确认机制
std::set<uint32_t> received_packets;
// 重传定时器
std::map<uint32_t, Timer> retransmit_timers;
// 流量控制
uint32_t send_window;
uint32_t recv_window;
public:
void send(const byte* data, size_t len);
void on_receive(const byte* data, size_t len);
void on_ack(uint32_t seq);
void on_timeout(uint32_t seq);
};
关键组件实现
数据包结构:
cpp
struct ReliablePacket {
uint32_t seq; // 序列号
uint32_t ack; // 确认号
uint16_t window; // 窗口大小
uint8_t flags; // 控制标志
byte data[0]; // 数据载荷
};
重传逻辑:
cpp
void ReliableUDP::send_packet(Packet& pkt) {
// 发送数据包
udp_send(pkt);
// 启动重传定时器
retransmit_timers[pkt.seq] = start_timer(RTO, [this, pkt]() {
this->on_timeout(pkt.seq);
});
// 加入未确认队列
unacked_packets[pkt.seq] = pkt;
}
拥塞控制:
cpp
void ReliableUDP::on_ack(uint32_t ack_seq) {
// 更新拥塞窗口
if (cwnd < ssthresh) {
// 慢启动
cwnd += MSS;
} else {
// 拥塞避免
cwnd += MSS * MSS / cwnd;
}
// 清理已确认的数据包
unacked_packets.erase(ack_seq);
retransmit_timers.erase(ack_seq);
}
9. 浏览器输入URL完整过程
步骤1: DNS解析
cpp
// 递归查询过程
1. 浏览器缓存: chrome://net-internals/#dns
2. 系统缓存: /etc/hosts (Linux), hosts文件(Windows)
3. 路由器缓存
4. ISP DNS服务器
5. 根DNS服务器 → 顶级域服务器 → 权威DNS服务器
步骤2: TCP连接建立
cpp
// 三次握手
客户端: SYN, seq=1000
服务器: SYN-ACK, seq=2000, ack=1001
客户端: ACK, seq=1001, ack=2001
// 如果是HTTPS,还有TLS握手
步骤3: HTTP请求
http
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0...
Accept: text/html,application/xhtml+xml
Accept-Encoding: gzip, deflate
Connection: keep-alive
步骤4: 服务器处理
python
# 服务器端处理流程
def handle_request(request):
# 1. 解析请求
method, path, headers = parse_request(request)
# 2. 路由处理
if path == '/index.html':
return serve_static_file('index.html')
elif path.startswith('/api/'):
return handle_api_request(path)
# 3. 生成响应
response = build_response(content, status_code=200)
return response
步骤5: 浏览器渲染
关键渲染步骤:
-
解析HTML构建DOM树:
html
<html> <head> <title>页面标题</title> <link rel="stylesheet" href="style.css"> </head> <body> <h1>标题</h1> <p>段落内容</p> </body> </html> -
解析CSS构建CSSOM:
css
h1 { color: red; font-size: 24px; } p { margin: 10px; } -
合并DOM和CSSOM生成渲染树:
text
渲染树: - html - body - h1 (color:red, font-size:24px) - p (margin:10px) -
布局计算:
cpp
// 计算每个元素的位置和大小 LayoutTree { html: {x:0, y:0, width:1200, height:800} body: {x:8, y:8, width:1184, height:784} h1: {x:20, y:20, width:1160, height:30} p: {x:20, y:60, width:1160, height:100} } -
绘制和合成:
-
将渲染树转换为像素
-
图层合成
-
GPU加速渲染
-
步骤6: 加载资源
javascript
// 浏览器并行加载资源
const resources = [
'style.css',
'script.js',
'image.jpg',
'font.woff2'
];
// 优先级调度
// 高: CSS, 阻塞渲染的JS
// 中: 图片, 字体
// 低: 异步JS, 预加载资源
步骤7: 连接管理
cpp
// HTTP/1.1 保持连接
Connection: keep-alive
Keep-Alive: timeout=5, max=100
// 或者关闭连接
Connection: close
10. GET vs POST 深度对比
GET请求特性
URL结构:
text
https://api.example.com/search?q=keyword&page=1&limit=10
协议 域名 路径 查询参数
缓存行为:
http
GET /api/users/123 HTTP/1.1
If-None-Match: "abc123"
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
HTTP/1.1 304 Not Modified // 使用缓存
安全性:
-
参数在URL中可见
-
可能被浏览器历史记录
-
可能被服务器日志记录
POST请求特性
请求体格式:
http
POST /api/users HTTP/1.1
Content-Type: application/json
Content-Length: 45
{"name":"John","email":"john@example.com"}
常见Content-Type:
-
application/x-www-form-urlencoded:name=John&email=john@example.com -
multipart/form-data: 文件上传 -
application/json: JSON数据 -
text/xml: XML数据
幂等性:
-
GET: 幂等(多次执行结果相同)
-
POST: 非幂等(可能产生副作用)
11. Cookie vs Session 详细机制
Cookie工作原理
设置Cookie:
http
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; Expires=Wed, 21 Oct 2020 07:28:00 GMT; HttpOnly; Secure
Set-Cookie: theme=dark; Max-Age=3600; Domain=.example.com; Path=/
发送Cookie:
http
GET /dashboard HTTP/1.1
Cookie: sessionId=abc123; theme=dark
Cookie属性:
javascript
const cookieAttributes = {
Expires: '过期时间',
Max-Age: '存活秒数',
Domain: '作用域名',
Path: '作用路径',
Secure: '仅HTTPS传输',
HttpOnly: '禁止JavaScript访问',
SameSite: 'CSRF防护'
};
Session工作原理
服务器端实现:
python
class SessionManager:
def __init__(self):
self.sessions = {} # session_id -> session_data
def create_session(self, user_id):
session_id = generate_unique_id()
self.sessions[session_id] = {
'user_id': user_id,
'created_at': time.now(),
'last_accessed': time.now()
}
return session_id
def get_session(self, session_id):
session = self.sessions.get(session_id)
if session:
session['last_accessed'] = time.now()
return session
Session存储方式:
-
内存存储:简单但服务器重启丢失
-
数据库存储:持久化,可分布式
-
Redis存储:高性能,支持过期
12. WebSocket握手详细过程
握手请求
http
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
关键头部:
-
Upgrade: websocket- 协议升级 -
Connection: Upgrade- 连接升级 -
Sec-WebSocket-Key- 客户端随机密钥 -
Sec-WebSocket-Version- 协议版本
握手响应
http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Accept计算:
javascript
function calculateAccept(key) {
const magicString = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
const hash = sha1(key + magicString);
return base64.encode(hash);
}
数据帧格式详解
text
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 len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
操作码含义:
-
0x0: 继续帧
-
0x1: 文本帧
-
0x2: 二进制帧
-
0x8: 连接关闭
-
0x9: Ping帧
-
0xA: Pong帧
13. HTTPS握手深度解析
TLS 1.2握手过程
步骤1: ClientHello
cpp
struct ClientHello {
ProtocolVersion client_version;
Random random;
SessionID session_id;
CipherSuite cipher_suites[];
CompressionMethod compression_methods[];
Extensions extensions;
};
步骤2: ServerHello
cpp
struct ServerHello {
ProtocolVersion server_version;
Random random;
SessionID session_id;
CipherSuite cipher_suite;
CompressionMethod compression_method;
Extensions extensions;
};
步骤3: 证书验证
cpp
// 证书链验证
bool verify_certificate_chain(Certificate[] chain) {
for (int i = 0; i < chain.length - 1; i++) {
if (!verify_signature(chain[i], chain[i+1].public_key)) {
return false;
}
}
return verify_root_ca(chain[chain.length-1]);
}
步骤4: 密钥交换
diff
客户端:
- 生成预主密钥
- 用服务器公钥加密
- 发送给服务器
服务器:
- 用私钥解密得到预主密钥
- 双方用随机数+预主密钥生成主密钥
- 派生出会话密钥
步骤5: 完成握手
cpp
// 双方交换Finished消息,验证握手过程完整性
Finished = HMAC(master_secret, all_handshake_messages)
TLS 1.3改进
主要变化:
-
减少到1-RTT握手
-
移除不安全的加密套件
-
密钥交换和身份验证合并
-
0-RTT模式(有重放攻击风险)
14. DNS解析详细过程
解析步骤
cpp
// 完整的DNS解析流程
DNSResolution resolve(const string& domain) {
// 1. 浏览器缓存
if (browser_cache.has(domain)) {
return browser_cache.get(domain);
}
// 2. 系统缓存 (hosts文件)
if (system_cache.has(domain)) {
return system_cache.get(domain);
}
// 3. 路由器缓存
if (router_cache.has(domain)) {
return router_cache.get(domain);
}
// 4. ISP DNS递归查询
return recursive_query(domain, "8.8.8.8");
}
DNS记录类型
cpp
enum DNSRecordType {
A = 1, // IPv4地址
AAAA = 28, // IPv6地址
CNAME = 5, // 规范名称
MX = 15, // 邮件交换
TXT = 16, // 文本记录
NS = 2, // 域名服务器
SOA = 6, // 起始授权机构
};
DNS报文格式
text
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 标识符 |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 问题数 | 答案RR数 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 权威RR数 | 附加RR数 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 问题区 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 答案区 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 权威区 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 附加区 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15. CDN工作原理深度解析
CDN架构
text
用户 → 边缘节点1 (北京)
→ 边缘节点2 (上海)
→ 边缘节点3 (广州)
↓
源服务器 (深圳)
缓存策略
cpp
class CDNCache {
public:
// 内容分发
void distribute(Content content, List<EdgeNode> nodes) {
for (auto node : nodes) {
if (should_cache(content, node)) {
node.cache(content);
}
}
}
// 缓存决策
bool should_cache(Content content, EdgeNode node) {
return content.popularity > threshold &&
node.has_capacity() &&
content.size < node.max_object_size;
}
};
负载均衡算法
cpp
// 基于地理位置的负载均衡
EdgeNode select_edge_node(User user) {
return edge_nodes
.min_by(node => distance(user.location, node.location))
.where(node => node.health == HEALTHY)
.where(node => node.load < MAX_LOAD);
}
动态内容加速
cpp
// 动态路由优化
Route optimize_route(User user, OriginServer origin) {
return routing_table
.find_optimal_path(user.network, origin.network)
.avoiding(congested_links);
}
16. 跨域问题(CORS)完整解决方案
简单请求 vs 预检请求
简单请求条件:
-
方法: GET, POST, HEAD
-
头部: 仅限简单头部
-
Content-Type: text/plain, multipart/form-data, application/x-www-form-urlencoded
预检请求流程:
text
1. 浏览器发送OPTIONS预检请求
2. 服务器返回允许的CORS头部
3. 浏览器发送实际请求
4. 服务器返回实际响应
CORS头部详解
http
# 预检响应
HTTP/1.1 200 OK
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
Access-Control-Allow-Credentials: true
# 实际响应
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Expose-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
服务器端实现
python
class CORSMiddleware:
def process_request(self, request):
if request.method == 'OPTIONS':
response = HttpResponse()
response['Access-Control-Allow-Origin'] = 'https://example.com'
response['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
response['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
response['Access-Control-Max-Age'] = '86400'
return response
def process_response(self, request, response):
response['Access-Control-Allow-Origin'] = 'https://example.com'
response['Access-Control-Allow-Credentials'] = 'true'
return response
17. 长连接 vs 短连接性能分析
短连接开销计算
cpp
// 建立TCP连接开销
Cost short_connection_cost =
TCP_handshake_latency +
TLS_handshake_latency +
TCP_slow_start_time +
Connection_teardown_latency;
// 示例: 100ms RTT网络
// 三次握手: 1.5 RTT = 150ms
// TLS握手: 2 RTT = 200ms
// 慢启动: 1 RTT = 100ms
// 总延迟: 450ms
长连接优势
cpp
// 连接复用
class ConnectionPool {
Map<Endpoint, Queue<Connection>> pools;
Connection get_connection(Endpoint endpoint) {
if (pools[endpoint].empty() ||
pools[endpoint].front().is_expired()) {
return create_new_connection(endpoint);
}
return pools[endpoint].pop();
}
};
HTTP/1.1管道化限制
text
客户端: 请求1 → 请求2 → 请求3
服务器: 处理1 → 处理2 → 处理3
响应: 响应1 → 响应2 → 响应3 // 必须按顺序
HTTP/2多路复用优势
text
客户端: 请求A1 → 请求B1 → 请求A2 → 请求B2
服务器: 响应B1 → 响应A1 → 响应A2 → 响应B2 // 可以乱序
18. HTTP缓存机制完整解析
强缓存
Cache-Control指令:
http
Cache-Control: max-age=3600, public, must-revalidate
-
max-age: 缓存有效期(秒) -
public: 允许任何缓存存储 -
private: 仅允许用户浏览器缓存 -
no-cache: 缓存但需要验证 -
no-store: 禁止缓存 -
must-revalidate: 必须验证过期缓存
Expires:
http
Expires: Wed, 21 Oct 2020 07:28:00 GMT
协商缓存
Last-Modified/If-Modified-Since:
http
# 首次请求
GET /resource HTTP/1.1
HTTP/1.1 200 OK
Last-Modified: Wed, 21 Oct 2020 07:28:00 GMT
# 后续请求
GET /resource HTTP/1.1
If-Modified-Since: Wed, 21 Oct 2020 07:28:00 GMT
HTTP/1.1 304 Not Modified # 资源未修改
ETag/If-None-Match:
http
# 首次请求
GET /resource HTTP/1.1
HTTP/1.1 200 OK
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
# 后续请求
GET /resource HTTP/1.1
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
HTTP/1.1 304 Not Modified
缓存决策流程图
text
请求资源
↓
检查Cache-Control
↓
如果max-age未过期 → 使用缓存
↓
如果过期或no-cache → 发送验证请求
↓
带If-Modified-Since或If-None-Match
↓
服务器返回304或200
19. RESTful API设计最佳实践
资源设计原则
rest
// 好的RESTful设计
GET /users # 获取用户列表
POST /users # 创建新用户
GET /users/{id} # 获取特定用户
PUT /users/{id} # 更新用户(完整替换)
PATCH /users/{id} # 部分更新用户
DELETE /users/{id} # 删除用户
GET /users/{id}/orders # 获取用户的订单
HTTP状态码使用
python
def handle_user_request(request, user_id):
if request.method == 'GET':
user = get_user(user_id)
if not user:
return 404, {'error': 'User not found'}
return 200, user
elif request.method == 'POST':
if create_user(request.data):
return 201, {'id': new_user_id}
else:
return 400, {'error': 'Invalid data'}
版本管理
http
# URL版本控制
GET /v1/users/123
GET /v2/users/123
# 头部版本控制
GET /users/123
API-Version: 2
HATEOAS(超媒体驱动)
json
{
"user": {
"id": 123,
"name": "John Doe",
"links": [
{
"rel": "self",
"href": "/users/123",
"method": "GET"
},
{
"rel": "orders",
"href": "/users/123/orders",
"method": "GET"
}
]
}
}
20. 负载均衡策略深度分析
轮询算法
cpp
class RoundRobinLoadBalancer {
vector<Server> servers;
atomic<int> current_index{0};
public:
Server select_server() {
int index = current_index.fetch_add(1) % servers.size();
return servers[index];
}
};
加权轮询
cpp
class WeightedRoundRobin {
vector<pair<Server, int>> servers; // server, weight
Server select_server() {
int total_weight = calculate_total_weight();
int random = rand() % total_weight;
for (auto& [server, weight] : servers) {
if (random < weight) {
return server;
}
random -= weight;
}
return servers[0].first;
}
};
最少连接
cpp
class LeastConnectionsLoadBalancer {
vector<pair<Server, atomic<int>>> servers; // server, connection_count
Server select_server() {
return min_element(
servers.begin(), servers.end(),
[](auto& a, auto& b) { return a.second < b.second; }
)->first;
}
};
一致性哈希
cpp
class ConsistentHashLoadBalancer {
map<uint32_t, Server> ring; // hash -> server
set<uint32_t> sorted_keys;
int virtual_nodes_per_server;
Server select_server(string key) {
uint32_t hash = hash_function(key);
auto it = sorted_keys.lower_bound(hash);
if (it == sorted_keys.end()) {
it = sorted_keys.begin();
}
return ring[*it];
}
};
健康检查机制
cpp
class HealthChecker {
map<Server, HealthStatus> server_status;
void check_health() {
for (auto& [server, status] : server_status) {
bool healthy = ping_server(server);
status.healthy = healthy;
status.last_check = now();
}
}
vector<Server> get_healthy_servers() {
vector<Server> healthy;
for (auto& [server, status] : server_status) {
if (status.healthy) {
healthy.push_back(server);
}
}
return healthy;
}
};
这些详细解答涵盖了网络协议和HTTP相关的核心知识点,应该能够帮助你深入理解这些重要概念。每个部分都包含了技术原理、实际应用和代码示例,让抽象的概念更加具体和易于理解。