08.*应用层、HTTP(七层)
HTTP
发送格式:

响应格式:

HTTPS
与HTTP相比,多了SSL/TLS 握手协议
概念:前向安全性:
- 密钥泄露后,即使拿到以前的记录,也无法通过密钥进行破解
- RSA、DH加密算法是非前向安全的;ECDHE是前向安全的
SSL/TLS 握手协议:
- 目的:
通过非对称加密算法,握手后协商出对称加密密钥
- 花费:
- 2 RTT
- TLS 1.2 通常情况下
- 1 RTT
- 使用会话复用技术后
- 0 RTT
- TLS 1.3
使用会话复用和
Pre-shared Key
(请求和Ticket一起发) #### TLS 密钥套件Ciper suite
:
- TLS 1.3
使用会话复用和
- 2 RTT
- 格式:
- TLS + 密钥协商算法 + 证书签名算法 + WITH + 对称加密算法 + 摘要算法
- 举例:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(TLS 1.2)
- 算法说明:
- 密钥协商算法:TLS阶段,用于生成对称密钥或者加密对称密钥的要素(要素加密)
- 签名算法:TLS阶段,用户客户端校验服务器证书有效性、ECDHE公钥的有效性(证书校验)
- 对称加密算法:TLS完成后,数据传输时加密(数据传输)
- 摘要算法:TLS最后阶段,对密钥协商过程数据做摘要并验证(过程验证)
- 举例:TLS_AES_256_GCM_SHA384(TLS 1.3)
- 密钥协商算法不在套件内,而是通过
key_share
扩展指定 - 签名算法不在套件内,而是通过
signature_algorithms
扩展指定
- 密钥协商算法不在套件内,而是通过
TLS 1.2 (ECDHE)握手过程:
*
TCP 握手、挥手过程在内核协议栈进行
,TLS在应用层
* TLS第一次握手
: * 客户端发 client hello
消息
* 消息包含:客户端TLS 版本号、支持的密码套件列表、客户端随机数 *
TLS第二次握手
: * 服务端发 server hello
消息 *
消息包含:协商后的TLS 版本号、协商后的协议套件、服务端随机数 * 服务端发
certificate
消息 * 消息包含:证书、证书对应的签名 *
服务端生成用于协议传输的公、私钥(基于协商的协议套件) * 服务端发
serverKeyExchange
消息(ECDHE有该过程,RSA没有) *
消息包含:ECDHE公钥、公钥签名、椭圆曲线的参数 * 服务端发
serverHelloDone
消息 *
消息包含:服务端协商相关数据已传输结束 * TLS第三次握手
: *
客户端验证证书有效性(通过CA或者OSCP stampling) *
客户端生成用于协议传输的公、私钥(基于协商的协议套件) * 客户端发
clientKeyExchange
消息 * ECDHE
消息包含:ECDHE公钥、公钥签名、椭圆曲线的参数 * RSA
消息包含:使用服务端证书公钥加密premaster secret
后的字符串
* 客户端发 changeCipherSpec
消息 *
计算出对称加密密钥,即会话密钥 *
ECDHE:双端公钥+椭圆曲线的参数+双端随机数 * RSA:pre-master
secret+双端随机数 * 消息包含:通知服务端后续使用对称加密 * 客户端发
Encrypted Handshake Message
消息 *
先使用摘要算法,计算协商过程产生的数据摘要 *
客户端再使用对称加密的会话密钥对摘要加密 * 消息包含:加密的摘要 *
TLS第四次握手
:(ECDHE时,客户端无需等待第四次完成即可发用户数据)
* 服务端发 changeCipherSpec
消息 * 使用私钥解密出pre-master
secret,计算出对称加密密钥,即会话密钥 *
ECDHE:双端公钥+椭圆曲线的参数+双端随机数 * RSA:pre-master
secret+双端随机数 * 消息包含:通知客户端后续使用对称加密 * 服务端发
Encrypted Handshake Message
消息 *
先使用摘要算法,计算协商过程产生的数据摘要 *
服务端再使用对称加密的会话密钥对摘要加密 * 消息包含:加密的摘要
TLS 1.2 和 1.3对比:
* 性能:TLS 1.2 握手4次,2RTT;TLS 1.3 握手2次,1RTT *
安全:TLS 1.3
将支持的密码套件缩减到五个,但都是
前向安全
的
TLS 握手性能优化:
- 硬件优化:
- 提升CPU性能(HTTPS协议是计算密集型)
- 软件优化:
- 软件升级:openssl
- 协议升级:
- 使用TLS 1.3
- 密钥交换算法使用
ECDHE
(替换RSA
)
- 证书优化:
- 证书传输:
- 选择使用
ECDSA
替换RSA
(前者协议数据更小)
- 选择使用
- 验证过程:
OCSP
在线证书状态协议:向CA查询证书的有效状态,按需查询。建议使用。CRL
证书吊销列表:向CA拉取证书吊销列表,一次查全量,但数据太大。OCSP Stapling
:- 原先为了验证的服务器的身份,服务器会在 TLS 握手过程中,把自己的证书发给客户端用于验证
- 使用
OCSP Stapling
,服务端向CA周期性地查询证书状态,在握手时返回给客户端,这样客户端就不用再去查询有效性了
- 证书传输:
- 工程优化:
- 会话复用:
- 含义:把首次TLS握手协商的对称加密密钥缓存起来,后续需要建立 HTTPS 连接时,直接复用
- 基于
Session ID
:- 服务端和客户端都缓存。使用唯一Session ID将密钥缓存到内存
- 基于
Session Ticket
:- 仅客户端缓存。客户端握手时上传
Session Ticket
,服务端解密验证
- 仅客户端缓存。客户端握手时上传
- 基于
Pre-shared Key
:- 在 TLS 1.3支持
- 客户端重连后,不进行握手,而是直接发
HTTP请求,将
Session Ticket
同时传给服务器
- 会话复用的缺点:
- 非前向安全
- 无法应对重放攻击
- 应对方法:需要设置好有效期 #### 实战经验:
- 会话复用:
- wireshark解析TLS加密报文:
- 修改系统环境变量:export SSLKEYLOGFILE=“/data/sslkeylogfile”
- 修改wireshark配置
- 【编辑】->【首选项】->【Protocols】->【TLS】->【Pre-Master Secret log】-> “/data/sslkeylogfile”
HTTP/1.0
- 略过
HTTP/1.1
- 基于TCP,使用文本进行传输 #### HTTP/1.1 的改进:
- 长连接:复用TCP连接,避免TCP握手的消耗
- 管道网络传输(pipeline):客户端的请求可以并发,但服务端响应需要有序(HTTP2.0 解决)
- 缓存策略:更复杂的缓存,协商缓存
- 大文件范围请求Range:断点续传
- 必须要Host头字段:一台服务器可以托管多个域名的网站
HTTP/1.1 存在的问题:
并发连接有限
:- 浏览器最大并发连接数是 6 个
- TCP 和 TLS 握手耗时
HTTP队头阻塞问题
:- 同一连接只能在完成一个 HTTP 事务(请求和响应)后,才能处理下一个事务
HTTP头部
巨大且重复- 不支持服务器推送消息,需要通过轮询方式
针对 HTTP/1.1 优化手段:
- 减少请求:
- 缓存数据、304 Not Modify
- 减少重定向次数
- 合并请求
- 按需获取
- 数据压缩:
- Accept-Encoding、Content-Encoding
- 无损压缩:gzip
- 有损压缩:webp、png、heif
HTTP/2.0
特征:
- 兼容HTTP/1.1,基于TCP,使用二进制传输
- 语义不改变,只是全部都是小写
- 语法改变大,HTTP报文的传输格式改变
- “h2”表示使用 TLS加密(HTTPS)的HTTP/2.0,“h2c”非加密(HTTP)
HTTP/2.0 的改进:
HPACK头部压缩
:
- 静态表编码:
- 共61组,HTTP/2.0协议内默认编码好了
- 动态表编码:
- 非静态表的头部,由客户端和服务端自行构建
- 仅对同一个TCP连接生效、当出现重复传输完全相同的HTTP头部时,才会起作用
- 存放在内存中,如果连接一直占用,可能会占用较高内存
- Huffman 编码(压缩算法)
- 存在的问题:
HPACK队头阻塞问题
:动态表发生变化+并发下,如果完整的编码未被接收,会导致后续压缩结果无法被发送,造成队头阻塞
二进制格式编码
:
HTTP/1.1 使用
文本
,HTTP/2.0 使用二进制
化整为零,将传输信息(header+body)分割为更小的帧
名词解释:
HEADERS 帧
:传输请求Header 内容DATA 帧
:传输请求正文(多个 Data帧属于同一个流,有先后顺序)Frame Header
:帧头(属于帧内的结构)Frame Payload
:帧数据(属于帧内的结构)
h2
包解析:
多路复用
:
- 含义:
- 一条 TCP 连接在同一时间可以跑多个Stream
- TCP connection、stream、message、frame的关系:
connection
可以有多个Stream
Stream
包含两个Message
,请求message
和响应message
Message
包含一个或者多个Frame
(Message 对应 HTTP/1 中的请求或响应)Frame
是 HTTP/2 最小单位,以二进制压缩存放 HTTP/1 中的头部和包体,Frame
是有顺序的,一个 Frame 可以由多个 TCP 报文构成`
服务器推送
(Server
Push/ Cache Push)
- 作用:
- 将可能会用到的数据先发送给客户端
缓存
下来
- 将可能会用到的数据先发送给客户端
- 服务端推送时创建的stream ID是偶数
- 客户端请求时创建的stream ID是奇数
解决HTTP的队头阻塞
- 通过
Stream
多路复用解决 - 同一个tcp连接中,http1的请求需要排队等待http事务完成后才能进行下一个(默认不开启
pipeline
,即使开启,服务端响应需要有序返回,这样也会阻塞); - http2使用流可以在同一个tcp中实现并发
HTTP/2.0 未解决的问题:
TCP队头阻塞问题
- 发生丢包时依赖tcp重传,得等到收到数据包后才能继续处理后续的流
网络迁移
需要重新建立TCP连接- 一个 TCP 连接是由四元组确定,IP地址或者端口变动,就需要TCP与 TLS重新握手(TCP存在连接状态导致,UDP无该问题)
帧协议格式:

- 帧长度:
- 单个帧最大2^24 = 16M
- 帧类型:
- 大致可以分成:
- 数据帧:HEADERS 、DATA
- 控制帧:SETTINGS、PING、PRIORITY等
- 此外 gRPC 定义了几种自用的新帧类型。
- 大致可以分成:
- 帧标志:
End Headers
:表示header传输结束,在HEADERS帧
使用End stream
:表示客户端或者服务端单方面数据传输结束,在HEADRS帧
(get)、DATA帧
(post)使用Padded
:表示该流是用来填充的,在任意帧都使用Ack
:表示确认,在PING帧
、SETTING帧
中使用
- 流标识符:
Stream ID
- 流 ID 是
递增
的:- 客户端发起的 ID 是奇数
- 服务器端发起的 ID 是偶数
- 流是
可并发
的:- 一个连接上可以同时有多个流,即“多路复用”也就是并发多请求;
- 流是
双向的
:- 客户端和服务器都可以创建流,双方互不干扰
- 一个流对应一次http请求、响应
- 流之间是
独立的
:- 但流内部的帧是有严格顺序的
- 流是有
优先级
的:- 在流上发送
PRIORITY帧
,让服务器优先处理某些流 - e.g. 先传 HTML/CSS,后传图片,优化用户体验
- 在流上发送
- 流是
可取消
的:- 在流上发送
RST_STREAM帧
可以随时终止流,取消接收或发送
- 在流上发送
- 第0号流比较特殊,不能关闭,也不能发送数据帧,只能发送控制帧,用于流量控制。
- 流 ID 是
数据收发:
- 从不同层对应的消息:
- 在“流”内看,消息由有序的“帧”序列组成
- 在“TCP”的层面上看,消息是乱序收发的“帧”
- 多个请求/响应之间没有了顺序关系,不需要根据请求顺序排队等待
- 多个请求/响应之间没有了顺序关系,不需要根据请求顺序排队等待
- 数据发送过程:
- 发送方将多个请求分到多个流中,然后将请求内容拆成帧,进行二进制传输
- 多个请求的帧,在传输层打散乱序发送
- 接收方根据每个帧首部的流标识符重新进行组装
- 可以根据优先级,决定优先处理哪个流的数据
- 因为TCP滑动窗口的原因,如果某个包没被确认,虽然后续包已到达,也不会被确认,而是一直阻塞
- 1.1 vs 2.0 发送示意图:
HTTP/3.0(HTTP-over-QUIC)
- 在HTTP/3.0中,数据流由传输层QUIC提供,而在HTTP/2中,流由HTTP层完成 ### QUIC:
- 特征:
- QUIC
基于UDP,是一个自己处理数据流的
传输层协议
,自身实现了TCP的一些特性 - QUIC 使用且必须用TLS 1.3传输层安全协议
- QUIC流 可以是单向的、也可以是双向的,由发起端决定
- QUIC 在用户空间中实现,使用QUIC意味着选择了套接字API之外的另一套API
- 建立过程:可能在首次握手时协商使用HTTP/2协议,通过Alt-Svc头部,服务器通告客户端它对HTTP/3的支持与偏好。所以首次连接可能会有TCP连接和UDP同时进行(跟DNS解析ipv4和ipv6同时进行类似)
- e.g. Alt-Svc: h3=“:50781”,建议客户端使用UDP端口50781提供的HTTP/3
- QUIC
基于UDP,是一个自己处理数据流的
解决HTTP/2.0 的问题:
- TCP 队头阻塞:
- QUIC的流基于UDP连接,每个流相互独立,互不影响(因为UDP不管顺序,不管丢包,所以传输层没有阻塞问题)
- TCP 建立连接的延迟更低:
- HTTP2.0:tcp + tls = 1RTT +2 RTT
- QUIC:传输层握手和tls合并
- 网络迁移(wifi和蜂窝互换)需要重新建立TCP连接
- HTTP2.0 基于TCP,tcp 五元组唯一确认一个连接
- QUIC
基于UDP,使用唯一的
connection id
,端口号同UDP端口
QUIC协议:
- 总览:
UDP Header Packet Header ----- QUIC Frame Header -- TLS1.3 HTTP3 Frame Header -- HTTP Message -----

- Packet(负责连接可靠):
- 定义
Connection ID
,类似TCP连接四元组- 64比特位(8字节)整数标识
Packet Number
实现了类似TCP的可靠的连接- 重传:当 UDP 报文丢失后,通过 Packet Header 中的 Packet Number 实现报文重传。
- Packet Number 单调递增,重传的包Packet Number不一样:
- RTT是精确的,没有TCP重传导致的RTT歧义
包可以被乱序确认
- 通过
Stream ID
+Offset
可以确认是同一个包,即使packet Number不一样
- 通过
Connection ID
实现了连接迁移:- 记录
Destination Connection ID
,在客户端 IP 地址、端口变化后,绕过 UDP 四元组,继续维持连接
- 记录
- 一个 Packet 报文中可以存放多个
QUIC Frame
,不能超过PMTUD(1200字节) - 上图Area Data即多个
QUIC Frame
,即在一个connection 中并发多个请求
- 定义
- QUIC Frame(负责字节流有序):
- 一个Frame不可跨越多个Packet传输
Frame Type
:- 定义不同类型的Frame
QUIC STREAM Frame
应用层数据传输other Frame
状态管理
- 定义不同类型的Frame
QUIC STREAM Frame
Stream Id
实现有序的字节流:- 在无序的 Packet
报文中,
Stream Id
相同表示同一个HTTP消息(消息过大时,可跨Packet传输) - 最低2位比特位用于识别连接的类型(单向、双向、发起者)
- 最低1比特位表示流的发起者:
- 0:双数流,客户端发起; 1:单数流,服务器发起
- 第2个比特位识别单/双向流:
- 0:双向;1:单向
- 最低1比特位表示流的发起者:
- 在无序的 Packet
报文中,
offset
用于重建字节流,实现消息的序列化:- 通过Offset字段完成类似于TCP协议中的Sequence序号,进行累计确认
Length
指明了Frame数据的长度- 以下为QUIC STREAM Frame格式:
- Stream Data即HTTP/3 Frame
- Stream Data即HTTP/3 Frame
- HTTP/3 Frame(负责HTTP语义):
Type
:类似 HTTP/2的帧类型:- DATA帧、HEADERS帧、SETTINGS帧、GOAWAY帧、MAX_PUSH_ID帧(限制服务器推送消息数量)
Frame Payload
:- 存放二进制的
HTTP message
- 存放二进制的
- 作用:
- 可跨越多个Packet
- 实现服务器推送
- 实现 QPACK 编解
- 权重优先级设定
- 流量控制
- HTTP Message(具体消息内容):
- 通用HTTP协议数据,header、body等
QUIC机制:
自定义连接:
- 不再以四元组标识,而是以一个64位的随机数作为Connection ID来标识
- 加密算法的版本协商与传输层握手合并完成,以减小延迟
自定义重传机制:
Packet Number
(数据包编号):在QUIC协议中,每个数据包都有一个唯一的编号,用于标识数据包在传输过程中的顺序和重传情况。Packet Number 用于确保数据包按正确的顺序传输,并在需要时进行重传
无阻塞的多路复用:
- 同一条 QUIC Connection Id上可以创建多个 stream 来发送多个请求(跟HTTP 2.0 一致)
QUIC为每个Stream分配独立的滑动窗口
。在传输层面如果出现丢包,只影响包所在的stream,其他stream无需等待。(跟HTTP2.0差异点,因为是基于UDP的)
自定义流量控制:
- stream级别的滑动窗口:
- QUIC 通过
window_update帧
来告诉对端它可以接受的字节数 - QUIC 窗口的起始位置为当前收到的最大 offset
- QUIC 的 ACK 是基于 offset 的,每个 offset 的包来了,进了缓存,就可以应答(相信中间的空档一定会到来)
QUIC为每个Stream分配独立的滑动窗口
,相互之间不影响 窗口示意图:TCP 和 QUIC- 可用窗口左边界:
- TCP:
已确认的
序号 - QUIC:
收到的
最大Offset
- TCP:
- QUIC 通过
- 连接级别的流量控制窗口:
- 对所有的stream做整体大小控制
服务器推送
(Server
Push/ Cache Push)
- 作用:
- 将可能会用到的数据先发送给客户端
缓存
下来,需要客户端同意
- 将可能会用到的数据先发送给客户端
- 过程:
- 服务器通过请求流发送一个
PUSH_PROMISE 帧
,使该推送请求看上去像是一个响应,然后通过新的流
发送实际推送数据 - 客户端可随时通过
CANCEL_PUSH
帧取消推送 - 客户端可以设置推送次数限制
- 服务器通过请求流发送一个
HTTP/3.0机制
头部压缩
:
- QPACK:
- 为什么要改HPACK:
- 由于动态表具有时序性,QUIC中数据流互相独立,并发下因为可能还未建立动态表会导致失败
- QPACK使用两个额外的单向QUIC流,用于在两个方向上
传递动态表信息
QPACK Encoder Stream
:用于传输动态表信息QPACK Decoder Stream
:用于对传输内容的确认响应
- 为什么要改HPACK:
二进制格式编码
:
- 同HTTP2.0相似,将传输信息(header+body)分割为更小的帧
HTTP/3.0 Frame:
* 帧类型与HTTP2基本相同 * 流的相关功能前置到传输层QUIC实现
* 头部压缩算法使用QPACK
对比:
* 参考: * https://http3-explained.haxx.se/zh/why-QUIC/why-tcphol
* https://xiaolincoding.com/network
* https://kiosk007.top/post/quic-%E5%8D%8F%E8%AE%AE/
本文作者:navyum
本文链接:https://www.cnblogs.com/navyum/p/18509330
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步