TCP、HTTP协议的深入研究
底层基础知识
通信模式
在计算机通信领域中,常见的模型默认有三种:单工模式、半双工模式、全双工模式
单工模式(Simplex)
- 定义:数据只能沿一个方向传输,一方固定为发送端,另一方固定为接收端,无法反向通信。
- 特点:信道是单向的,无需考虑双向同步或冲突问题,结构简单但灵活性低。
- 举例:广播电视(电视台发送信号,电视机只能接收)、键盘向电脑输入数据(键盘仅发送,电脑仅接收)、打印机接收电脑指令(打印机仅接收)。
半双工模式(Half-Duplex)
- 定义:数据可以双向传输,但同一时间内只能沿一个方向传输,即发送和接收不能同时进行,需要交替切换方向。
- 特点:共享同一信道,通信双方需要协商切换方向,可能存在一定的延迟(如等待对方释放信道)。
- 举例:对讲机(一方说话时另一方需等待,按下 “说话键” 时只能发送,松开后才能接收)、早期的以太网(使用同轴电缆时,同一时间只能有一台设备发送数据,避免冲突)、无线电报。
全双工模式(Full-Duplex)
- 定义:通信双方(或设备)可以在同一时间内同时发送和接收数据,即双向通信可并行进行,无需等待对方停止发送。
- 特点:需要两条独立的物理信道,分别负责“发送”和“接手”,并且发送和接收的操作可以同时进行。
- 举例:电话通话、现代以太网、即时视频通话、光纤通信
OSI七层模型与TCP/IP四层模型
OSI七层模型
OSI即开放式互联,该体系定义了网络互联的七层框架。OSI参考模型是一个具有七层结构的模型,发送和接收信息所涉及的内容和相应的设备称为实体,OSI的每一层都包含多个实体,处于同一层的实体称为对等实体。
七层协议(快速记忆法):物(物理层 Physical)、数(数据链路层 Data Link)、网(网络层 Network)、传(传输层 Transport)、会(会话层 Session)、表(表示层 Presentation)、应(应用层 Application)
TCP/IP四层概念模型
TCP/IP四层概念模型是对OSI七层模型的简化与优化,它参照OSI模型的分层概念,将一些功能相近的层进行了合并,最终形成的结果如下:
TCP/IP协议
TCP 提供面向有连接的通信传输,面向有连接是指在传送数据之前必须先建立连接,数据传送完成后要释放连接。
无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。同时由于TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议,TCP是全双工模式,所以需要四次挥手关闭连接。
TCP协议在网络模型中的位置
TCP 属于传输层协议,在不同网络模型中的定位如下:
- OSI 七层模型:位于第 4 层(传输层),下方依赖网络层(如 IP 协议)提供的主机到主机的分组传输服务,上方为应用层协议(如 HTTP、FTP、SMTP 等)提供可靠的数据传输支持。
- TCP/IP 四层模型:同样位于第 4 层(传输层),与 OSI 模型的传输层功能一致,是 TCP/IP 协议栈的核心协议之一(另一核心是网络层的 IP 协议)。
TCP报文格式
TCP 报文(段,Segment)由首部和数据部分组成,首部最小长度为 20 字节,最大为 60 字节(含选项字段)。
| 字段 | 长度(字节) | 作用说明 |
|---|---|---|
| 源端口 | 2 | 发送端的端口号,用于标识发送进程。 |
| 目的端口 | 2 | 接收端的端口号,用于标识接收进程。 |
| 序列号 | 4 | 对发送的字节流编号(按字节计数),用于保证数据有序性和去重(如首次发送 SYN 时,序列号为初始 ISN)。 |
| 确认号 | 4 | 期望收到的下一字节的序列号(仅当 ACK=1 时有效),用于确认已收到的数据。 |
| 数据偏移 | 4 位(0.5 字节) | 表示 TCP 首部长度(单位:32 位字,即 4 字节),用于区分首部和数据部分(最小值为 5,对应 20 字节)。 |
| 保留位 | 6 位 | 预留未使用,通常为 0。 |
| 控制位 | 6 位 | 共 6 个标志位,分别为: - URG:紧急指针有效; - ACK:确认号有效(握手 / 挥手核心); - PSH:立即推送数据给应用层; - RST:重置连接(用于错误恢复); - SYN:同步序列号(用于建立连接); - FIN:终止连接(用于关闭连接)。 |
| 窗口大小 | 2 | 接收窗口大小,用于流量控制(告知对方自己的接收缓冲区剩余容量)。 |
| 校验和 | 2 | 用于校验整个 TCP 报文(首部 + 数据)的完整性,检测传输错误。 |
| 紧急指针 | 2 | 当 URG=1 时有效,指示紧急数据在数据部分中的偏移量(优先传输紧急数据)。 |
| 选项(可选) | 0~40 | 用于扩展 TCP 功能(如最大报文段长度 MSS、窗口扩大因子、时间戳等)。 |
| 数据部分 | 可变 | 应用层传递的实际数据(长度受 MSS 限制,避免 IP 分片)。 |
TCP 三次握手(建立连接)
TCP 是面向连接的协议,通信前必须通过 “三次握手” 建立可靠连接。核心目的是:同步双方的初始序列号(ISN)、确认双方的收发能力。
详细过程如下:
- 第一次握手(客户端→服务端):
- 客户端主动发起连接,发送报文:SYN=1(同步标志),seq=x(客户端初始序列号 ISN_c)。
- 客户端状态:从CLOSED→SYN_SENT。
- 第二次握手(服务端→客户端):
- 服务端收到 SYN 后,同意连接,回复报文:SYN=1(同步标志),ACK=1(确认标志),seq=y(服务端初始序列号 ISN_s),ack=x+1(确认已收到客户端的 x,期望下一字节为 x+1)。
- 服务端状态:从LISTEN→SYN_RCVD。
- 第三次握手(客户端→服务端):
- 客户端收到 SYN+ACK 后,发送报文:ACK=1,seq=x+1(客户端序列号递增),ack=y+1(确认已收到服务端的 y,期望下一字节为 y+1)。
- 客户端状态:从SYN_SENT→ESTABLISHED;服务端收到后状态:从SYN_RCVD→ESTABLISHED。
三次握手可确保双方 “发送能力” 和 “接收能力” 均正常,且序列号同步完成。
TCP 四次挥手(终止连接)
TCP 连接是全双工的,关闭时需双方分别终止各自的发送方向,因此需要 “四次挥手”。
详细过程如下:
- 第一次挥手(客户端→服务端):
- 客户端主动关闭连接,发送报文:FIN=1(终止标志),seq=u(客户端当前序列号)。
- 客户端状态:从ESTABLISHED→FIN_WAIT_1(等待服务端确认)。
- 第二次挥手(服务端→客户端):
- 服务端收到 FIN 后,回复确认:ACK=1,seq=v(服务端当前序列号),ack=u+1(确认已收到客户端的 FIN)。
- 此时客户端到服务端的方向关闭,但服务端仍可向客户端发送数据;服务端状态:从ESTABLISHED→CLOSE_WAIT;客户端状态:从FIN_WAIT_1→FIN_WAIT_2(等待服务端的 FIN)。
- 第三次挥手(服务端→客户端):
- 服务端完成数据发送后,主动关闭自己的发送方向,发送报文:FIN=1,ACK=1,seq=w(服务端当前序列号),ack=u+1。
- 服务端状态:从CLOSE_WAIT→LAST_ACK(等待客户端确认)。
- 第四次挥手(客户端→服务端):
- 客户端收到 FIN 后,回复确认:ACK=1,seq=u+1,ack=w+1。
- 客户端状态:从FIN_WAIT_2→TIME_WAIT(等待 2MSL,确保服务端收到确认);服务端收到后状态:从LAST_ACK→CLOSED。
- 客户端等待 2MSL(报文最大生存时间)后,状态从TIME_WAIT→CLOSED。
关闭连接时,双方需各自终止发送方向。服务端收到 FIN 后,可能还有数据未发送,因此先回复 ACK(第二次挥手),待数据发送完毕后再发 FIN(第三次挥手),故无法合并为三次。
HTTP协议概述与发展历程
HTTP(HyperText Transfer Protocol)是互联网上应用最为广泛的网络协议,用于客户端和服务器之间的通信。自1996年HTTP/1.0发布以来,HTTP协议经历了多次重大升级,其中HTTP/1.1成为了长期稳定的版本,而HTTP/2则是在2015年正式发布的重大升级版本。
HTTP协议的核心特点是请求-响应模型,客户端发起请求,服务器返回响应。随着互联网内容的日益丰富和复杂,对HTTP协议的性能要求也越来越高。HTTP/2正是为了解决HTTP/1.x的性能瓶颈而设计的。
HTTP/1.x系列协议
HTTP/1.x系列协议包括HTTP/1.0和HTTP/1.1,其中HTTP/1.1于1999年发布,是目前使用最广泛的HTTP版本。HTTP/1.1引入了持久连接、管道化等重要特性,显著提升了HTTP协议的性能。
HTTP/2的诞生背景
随着Web应用的复杂化,HTTP/1.x的性能瓶颈逐渐显现:
- 队头阻塞问题:在同一TCP连接上,如果一个请求处理缓慢,后续请求必须等待,造成性能下降。
- 头部冗余:每个请求都需要重复发送相似的头部字段,浪费带宽。
- 二进制协议缺失:文本格式的协议解析效率低,且难以实现高级功能。
HTTP/2正是为了解决这些问题而设计的,它基于Google的SPDY协议,于2015年以RFC 7540正式发布。
HTTP/1.x系列协议
HTTP/1.x系列的工作原理与数据流转
HTTP/1.0(短连接模式)
在短连接模式下,每个请求/响应都需要建立一个新的TCP连接:
- 客户端与服务器建立TCP连接(三次握手)
- 客户端发送请求报文
- 服务器返回响应报文
- 连接关闭(四次挥手)
这种模式的缺点是连接建立和关闭开销大,特别是在请求频繁的情况下,性能会显著下降。
HTTP/1.1(持久连接)
HTTP/1.1引入了持久连接(Persistent Connection),通过在请求头中添加Connection: keep-alive,允许在同一个TCP连接上发送多个请求/响应:
- 客户端与服务器建立TCP连接
- 客户端发送多个请求,服务器依次返回响应
- 所有请求处理完毕后,连接可以保持一段时间,供后续请求使用
持久连接减少了TCP连接建立和关闭的开销,但仍然存在队头阻塞问题——当前一个请求未完成时,后续请求必须等待。
管道化(Pipelining)
HTTP/1.1还引入了管道化(Pipelining)技术,允许在未收到前一个响应的情况下发送下一个请求:
- 客户端在一个TCP连接上连续发送多个请求
- 服务器按顺序返回响应
然而,管道化在实际应用中并未得到广泛支持,主要原因是它仍然无法解决队头阻塞问题,且实现复杂度高。
HTTP/1的请求-响应模型及报文格式
HTTP/1采用严格的请求-响应模型,客户端必须等待前一个请求的响应完全接收后才能发送下一个请求(除非使用管道化)。
请求报文结构
HTTP/1请求报文由四个部分组成:
- 请求行
- 头部字段
- 空行
- 请求体(可选)
请求行格式与示例:
1 | 方法 URI HTTP版本 |
头部字段是键值对形式,用于传递附加信息,比如:
1 | Host: example.com |
响应报文结构
HTTP/1响应报文同样由四个部分组成:
- 状态行
- 头部字段
- 空行
- 响应体(可选)
状态行格式与示例:
1 | HTTP版本 状态码 原因短语 |
HTTP/1的数据流转方式
HTTP/1的数据流转基于文本格式,采用串行传输方式:
- 客户端与服务器建立TCP连接
- 客户端将请求报文转换为文本字符串,通过TCP连接发送
- 服务器接收文本字符串,解析出请求内容
- 服务器处理请求,生成响应报文
- 服务器将响应报文转换为文本字符串,通过TCP连接发送
- 客户端接收文本字符串,解析出响应内容
- 连接关闭(短连接)或保持(持久连接)
这种方式的主要问题在于:
- 文本格式效率低:文本解析需要额外处理,且头部字段重复传输
- 队头阻塞:同一连接上的请求必须按顺序处理
- 连接利用率低:每个连接同一时间只能处理一个请求
HTTP/2协议
HTTP/2 是 HTTP 协议的重大升级,它保留了 HTTP/1 的语义,但在底层传输机制上进行了革命性改进。
核心特性
二进制分帧层
HTTP/2摒弃了HTTP/1的文本格式,转而采用二进制分帧层(Binary Framing Layer):
- 将整个通信分解为帧(Frame)的交换
- 每个帧携带特定类型的数据(如头部、正文、设置等)
- 帧是最小的传输单位,通过流标识符(Stream ID)关联到特定的流
上图分别是HTTP1.1与HTTP2.0的构造图,从这两张图可以看出,HTTP1.1是明文文本,而 HTTP2.0首部(HEADERS)和数据消息主体(DATA)都是帧(frame)。frame是HTTP2协议中最小数据传输单元。
帧(frame)主要组成部分如下:
- 帧头(Headers Frame):固定的9个字节
- 长度(Length):无符号的自然数,24个比特表示,仅表示帧负载(Frame Payload)所占用字节数,不包括帧头所占用的9个字节。 默认大小区间为为0~16,384(2^14),一旦超过默认最大值2^14(16384),发送方将不再允许发送,除非接收到接收方定义的
- 类型(Type):8个比特表示,定义了帧负载的具体格式和帧的语义,HTTP/2规范定义了10个帧类型,这里不包括实验类型帧和扩展类型帧,常见类型如下:
- 0x0:DATA 帧(传输流的核心内容)
- 0x1:HEADERS 帧(包含 HTTP 首部)
- 0x2:PRIORITY 帧(指示或更改流的优先级)
- 0x3:RST_STREAM 帧(停止流)
- 0x4:SETTINGS 帧(协商连接级参数)
- 0x5:PUSH_PROMISE 帧(服务器推送)
- 0x6:PING 帧(测试连接可用性)
- 0x7:GOAWAY 帧(结束连接)
- 0x8:WINDOW_UPDATE 帧(流量控制)
- 0x9:CONTINUATION 帧(扩展 HEADERS 数据块)
- 标记(Flags):8个比特表示,具体含义由帧类型决定。
- DATA 帧的 END_STREAM 标志(位 0):表示该帧是流的最后一个帧。
- HEADERS 帧的 PADDED 标志(位 3):表示帧包含填充数据。
- R:1个比特表示,保留位,必须设置为 0。非 0 值会导致协议错误(PROTOCOL_ERROR)。
- 流标识(Stream Identifier):无符号的31比特表示,无符号自然数。0x0值表示为帧仅作用于连接,不隶属于单独的流。遵循如下规则:
- 客户端发起的流 ID 为奇数(如 1, 3, 5…)。
- 服务端发起的流 ID 为偶数(如 2, 4, 6…)。
- 如果流 ID 为 0,则表示该帧不属于任何流(如 SETTINGS、PING 等帧)。
- 有效载荷(frame payload):长度帧头中的 Length 字段决定,承载具体的帧内容,不同类型的帧有不同的负载格式。示例如下
- DATA 帧:包含流的二进制数据。
- HEADERS 帧:包含经过 HPACK 压缩的 HTTP 首部字段。
- PING 帧:包含用于测试连接的 8 字节 payload。
| 字段 | 长度 | 作用 |
|---|---|---|
| Length | 3 字节 | 表示帧负载的长度(单位:字节) |
| Type | 1 字节 | 标识帧的类型(如 DATA、HEADERS 等) |
| Flags | 1 字节 | 标志位,具体含义由帧类型决定 |
| R | 1 比特 | 保留位,必须为 0,否则触发协议错误 |
| Stream Identifier | 31 比特 | 标识帧所属的流 ID,用于多路复用 |
多路复用
HTTP/2通过多路复用(Multiplexing)彻底解决了队头阻塞问题:
- 多个请求/响应可以在同一连接上并发传输
- 每个请求/响应被分配一个唯一的流ID(Stream ID)
- 帧可以交错发送,接收方根据流ID重新组装
这意味着在单个TCP连接上可以同时处理多个请求,大大提高了连接利用率(类似于连接池)。
上图就是HTTP/1.X和HTTP/2.0在传输数据时的区别。显然,第二种方式运输数据多,资源的利用率高。
头部压缩
HTTP/1.X的头带有大量信息,而且每次都要重复发送。HTTP/2使用encoder来减少需要传输的header大小,通讯双方各自缓存一份头部字段表,既避免了重复header的传输,又减小了需要传输的大小。
另一方面HTTP/2还引入了HPACK头部压缩算法,相比较HTTP/1.X则不支持压缩。
- 建立共享的静态和动态字典
- 重复的头部字段通过字典索引表示
- 新增的头部字段会被动态添加到字典中
- 使用Huffman编码进一步压缩头部数据
HPACK压缩显著减少了头部数据的传输量,降低了带宽消耗。
简单介绍压缩原理:
- 用Header字段表里的索引代替实际的header:HTTP/2的HPACK算法使用一份索引表来定义常用的Header,把常用的http Header存放在表里,请求的时候便只需要发送在表里的索引位置即可。
- 将字符串进行霍夫曼编码来压缩字符串大小:以常用的 User-Agent 为例,它在静态表中的索引值是 58,它的值是不存在表中的,因为它的值是多变的。第一次请求的时候它的 key 用 58 表示,表示这是一个 User-Agent ,它的值部分会进行霍夫曼编码(如果编码后的字符串变更长了,则不采用霍夫曼编码)。
服务器推送
HTTP/2允许服务器主动向客户端推送资源:
- 服务器可以根据客户端的初始请求,预判其可能需要的其他资源
- 通过发送PUSH_PROMISE帧,服务器可以推送相关资源(如CSS、JS文件)
- 客户端可以选择接受或拒绝推送的资源
这一特性减少了额外的往返次数,加快了页面加载速度。
- 注意事项:
- 推送遵循同源策略;
- 这种服务端的推送是基于客户端的请求响应来确定的。
请求优先级
把HTTP消息分为很多独立帧之后,就可以通过优化这些帧的交错和传输顺序进一步优化性能。每个流都可以带有一个31比特的优先值:0 表示最高优先级;2的31次方-1 表示最低优先级。
服务器可以根据流的优先级,控制资源分配(CPU、内存、带宽),而在响应数据准备好之后,优先将最高优先级的帧发送给客户端。高优先级的流都应该优先发送,但又不会绝对的。绝对地准守,可能又会引入首队阻塞的问题:高优先级的请求慢导致阻塞其他资源交付。
- 优先级最高:主要的html
- 优先级高:CSS文件
- 优先级中:js文件
- 优先级低:图片、视频等其他资源
HTTP/1与HTTP/2的对比
总体结构差异
- HTTP/1的文本格式:HTTP/1的报文采用纯文本格式,可读性强但效率低
- 请求和响应由文本行组成
- 头部字段和值都是明文形式
- 依赖特定的分隔符(如CRLF)来标识字段结束
- HTTP/2的二进制格式:HTTP/2采用二进制格式,效率高但不可直接阅读
- 所有数据以二进制帧形式传输
- 头部数据经过HPACK压缩
- 不同类型的数据(头部、正文、控制信息)使用不同的帧类型
头部处理差异
- HTTP/1的头部处理:HTTP/1的头部处理存在明显缺陷
- 每个请求都必须重复发送相同的头部字段(如Host、User-Agent)
- 头部字段以明文形式传输,增加了传输量
- 头部字段的顺序不固定,可能影响缓存效率
- HTTP/2的头部处理:HTTP/2通过HPACK算法解决了头部处理的问题:
- 使用共享字典减少重复传输
- 头部字段和值被压缩为二进制格式
- 头部块被拆分为多个帧传输(如果超过一帧的大小)
- 头部处理更加高效,减少了传输量和处理时间
数据传输差异
- HTTP/1的数据传输:HTTP/1的数据传输基于文本,采用串行方式
- 每个请求/响应在单独的连接或流中传输
- 同一连接上的请求必须按顺序处理
- 大文件传输可能导致后续请求长时间等待(队头阻塞)
- HTTP/2的数据传输:HTTP/2的数据传输基于二进制帧,实现了真正的并发
- 多个请求/响应可以在同一连接上并发传输
- 数据被拆分为多个帧,通过流ID关联
- 接收方根据流ID重新组装数据
- 可以设置流的优先级,优化资源分配
性能对比
连接建立性能对比
HTTP/1和HTTP/2都基于TCP连接,但HTTP/2通常要求TLS加密:
- HTTP/1.1的纯文本连接只需要TCP三次握手
- HTTP/1.1的HTTPS连接需要TCP握手+TLS握手(通常需要2-RTT)
- HTTP/2的HTTPS连接同样需要TCP握手+TLS握手,但可以通过ALPN协商协议版本
吞吐量与延迟对比
测试表明,HTTP/2在大多数情况下吞吐量高于HTTP/1:
- HTTP/2的多路复用特性允许在单个连接上并发处理多个请求
- HTTP/1的持久连接虽然可以重用连接,但同一时间只能处理一个请求
- 在高并发场景下,HTTP/2的吞吐量优势更加明显
安全性对比
- HTTP/1本身不要求加密,HTTPS是可选的
- HTTP/1.1可以在纯文本(HTTP)或加密(HTTPS)模式下运行
- 许多网站仍然使用纯文本HTTP,存在窃听和篡改风险
- 即使使用HTTPS,HTTP/1的加密实现也可能存在安全漏洞
- HTTP/2通常要求加密:
- 现代浏览器只支持TLS加密下的HTTP/2
- HTTP/2通过TLS的ALPN扩展协商协议版本
- 强制加密减少了中间人攻击的风险
特殊场景性能对比
- 大文件传输:在大文件(如视频、大图片)传输场景中,HTTP/2可能表现不如HTTP/1
- HTTP/2的多路复用可能导致单个大文件占用过多带宽
- HTTP/1的持久连接在大文件传输时可能更高效
- 低带宽网络:在低带宽网络环境下,HTTP/2的优势更加明显
- HPACK头部压缩减少了不必要的传输量
- 多路复用减少了连接建立开销
- 服务器推送减少了额外的往返次数
- 单页应用(SPA):单页应用通常包含大量的JavaScript和API请求
- HTTP/2的多路复用减少了多个API请求的延迟
- 头部压缩减少了重复的头部传输
- 服务器推送可以预加载关键资源
- 移动应用:移动应用通常面临网络不稳定和带宽受限的问题:
- HTTP/2的头部压缩减少了数据传输量
- 多路复用减少了连接建立次数
- 在高延迟网络环境下表现更好
综合对比
| 特性 | HTTP/1 | HTTP/2 |
|---|---|---|
| 协议格式 | 文本格式 | 二进制分帧 |
| 连接管理 | 持久连接(默认) | 多路复用 |
| 头部处理 | 明文传输,重复发送 | HPACK压缩,共享字典 |
| 并发处理 | 管道化(有限支持) | 真正的并发 |
| 服务器推送 | 不支持 | 支持 |
| 安全性 | 可选加密(HTTPS) | 通常要求加密(TLS) |
| 性能 | 较低,存在队头阻塞 | 较高,解决了队头阻塞 |