传输层协议

TCP 协议 和 UDP 协议

一个进程可不可以绑定(bind) 多个端口号?

可以通过端口号访问某个进程,但是 不能一个端口号不能绑定多个进程

TCP和UDP 可以使用同一个端口吗?

TCP 和 UDP 可以使用同一个端口。TCP 和 UDP 是两种不同的传输层协议,它们在传输数据时使用不同的机制。由于它们是独立的协议,因此可以在同一台设备上使用相同的端口号而不会发生冲突。

TCP 和 UDP 可以使用相同的端口号,因为它们在操作系统中是通过不同的协议栈进行管理的。例如,您可以在 TCP 端口 80 上运行一个 HTTP 服务器,同时在 UDP 端口 80 上运行一个不同的服务。

操作系统会根据协议类型(TCP 或 UDP)和端口号来区分不同的连接和数据传输。因此,即使 TCP 和 UDP 使用相同的端口号,它们的连接和数据传输也是独立的,不会发生冲突。

知名端口号

执行下面的命令, 可以看到知名端口号 cat /etc/services

🤓☝️cat /etc/services | head -10                                                                                16:45:09
# Full data: /usr/share/iana-etc/port-numbers.iana
​
tcpmux              1/tcp
tcpmux              1/udp
compressnet         2/tcp
compressnet         2/udp
compressnet         3/tcp
compressnet         3/udp
rje                 5/tcp
rje                 5/udp

0 - 1023: 知名端口号 端口号是 约定好的

natstat 用来查看网络状态的 重要工具

使用方法 natstat []

功能查看网络状态 :

  • n 拒绝显示别名

  • l 仅列出 在Listen (监听)的服务状态

  • p 显示 建立相关链接的程序名

  • t tcp 仅显示tcp选项

  • u udp 仅显示udp 选项

  • a all 显示 所有选项 默认不显示 LISTEN 相关

查看服务器 进程 id pidof 【进程名】

UDP协议 用户数据报协议

UDP(用户数据报协议)是一种无连接的传输层协议,特点:

  • 无连接:无需建立连接即可发送数据。

  • 不可靠:不保证数据包的顺序和完整性。

  • 轻量级:头部开销小,适用于实时应用。

UDP协议端格式

16位源端口号

16位目的端口号

16位UDP 长度

16位UDP检验和

数据(可空)

数据(可空)

  • 16位 udp 长度 整个数据报文udp 首部 + udp数据的最擦长度

  • 校验出错直接丢弃

UDP 是 面向数据报的

报头其实就是 一重结构化数据对象、Linux 是 使用C 语言写的 使用结构体存储 序列化 反序列化

协议段格式就包含在数据报文结构体中

应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并;

用UDP传输100个字节的数据: 如果发送端调用一次sendto, 发送100个字节, 那么接收端也必须调用对应的一次recvfrom, 接收100个 字节; 而不能循环调用10次recvfrom, 每次接收10个字节;

UDP 的缓冲区

UDP没有真正意义上的发送缓冲区。当调用 sendto 函数时,数据会直接交给内核,由内核将数据传递给网络层协议进行后续的传输操作。这意味着数据在发送时不会在用户空间进行缓冲,而是立即进入内核进行处理。

UDP 具有接收缓冲区,用于存储从网络层接收到的数据报。

接收缓冲区不能保证接收到的 UDP 数据报的顺序与发送顺序一致。

由于 UDP 是无连接的协议,不提供数据包顺序控制和重传机制,因此数据报可能会乱序到达。

如果接收缓冲区满了,新的 UDP 数据报将会被丢弃。 造成数据丢失

传输超过 64KB 数据的解决方案

如果需要传输的数据超过 64KB,就需要在应用层手动进行分包和拼装。

分包

  • 将大数据分割成多个小数据包,每个数据包的大小不超过 64KB。

  • 为每个数据包添加序号,以便接收端能够正确拼装。

发送

  • 逐个发送分割后的数据包。

  • 确保每个数据包都能独立传输,不依赖于其他数据包。

接收

  • 接收端逐个接收数据包,并根据序号进行排序。

  • 将接收到的数据包拼装成完整的数据。

TCP 传输控制协议

  • TCP(传输控制协议)是一种面向连接的、可靠的传输层协议,广泛用于互联网通信。

  • 特点:

    • 面向连接

    • 可靠传输

    • 流量控制

    • 拥塞控制

    • 全双工通信

    • 数据校验

    • 数据分段重组

TCP 协议段格式

16位源端口号

16位目的端口号

32位

序号

32位

确认序号

4位首部长度 + 保留六位 + 六位标志位

16位窗口大小

16位检验和

16位紧急指针

选项

选项

数据

数据

TCP 协议报头是有标准长度的:20

  • 读取20 字节 转换成一个结构化数据,立马提取标准报头中的 4首部长度

  • tcp 报头的 总长度0000- 1111 【0,15】

  • TCP 报文的总长度 = 4位首部长度 * 4 字节

  • TCP 报头的 总长度 【0,15】

  • x*4 - 20=0; x * 4 - 20 = n;

  • 【20-60】 就是 有效载荷的 长度

TCP的 确认应答机制

每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发

双方 进行通讯的时候 除了 正常的数据段 通信的时候也会涵盖确认数据段

Linux 是 使用C 语言写的 使用结构体存储 序列化 反序列化

协议段格式就包含在数据报文结构体中

如何封装解包 如何分用

通过目标 端口号就可以找到 应用层的进程 数据就可以交付给进程

如何理解 TCP 的报头

和 UDP 一样 、Linux 是 使用C 语言写的 使用结构体存储 序列化 反序列化协议段格式就包含在数据报文结构体中。

TCP的 6位标记位

  • URG: 紧急指针

  • ACK:确认号

  • PSH: 提示接受端应用 立刻从TCP 数据拿走

  • RST:要求对方重新建立连接 复位报文段

  • SYN : 请求建立连接 同步报文段

  • FIN:关闭此端 结束报文段

为什么 TCP 有 两个 32位序号?

TCP 协议使用两个 32 位序号字段:序列号(Sequence Number)和确认号(Acknowledgment Number)

  • TCP 数据段 需要 标识 数据段本身 注定应答报文 ,对应报头中必定涵盖了 确认序号!

  • 序列号(Sequence Number)

    • 功能:序列号用于标识 TCP 数据段中的每个字节。发送方为每个数据段分配一个序列号,接收方使用该序列号来重组数据段,确保数据按正确顺序到达。

    • 工作原理:每个 TCP 连接在建立时,双方会交换初始序列号(Initial Sequence Number, ISN)。之后,发送方每发送一个字节,序列号就增加 1。例如,如果初始序列号为 1000,发送了 100 字节的数据,那么下一个数据段的序列号将是 1100。

    确认号(Acknowledgment Number)

    • 功能:确认号用于确认接收到的数据。接收方在收到数据段后,会发送一个带有确认号的 ACK 数据段,告知发送方已经成功接收到的数据。

    • 工作原理:确认号表示接收方期望接收的下一个字节的序列号。例如,如果接收方收到的最后一个字节的序列号是 1099,那么确认号将是 1100,表示接收方期望接收从 1100 开始的数据。

  • 因为 TCP 是全双工的 一个 用于返送 一个 用于接收

16位 窗口大小

在TCP 发送的时候 快/慢 都不太可以 如何得知对方的接收缓冲区的大小

16位窗口大小中 就是 自己的接收 缓冲区 剩余大小

通过16位窗口大小 获得 对方的缓冲区 剩余空间大小 对发送速率进行 动态调整 实现流量控制

超时重传机制

距离长了就不存在绝对的可靠性 但是 存在相对的可靠 可以相对保证数据的完整性

如何 判断 丢包了呢 ? 根据 指定的策略 进行 重传

根据TCP 的机制 出现 信息 丢失 一定是某一方数据 未收到数据 没有应答

主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B;

如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发

因此主机B会收到很多重复数据. 那么TCP协议需要能够识别出那些包是重复的包, 并且把重复的丢弃掉.

这时候我们可以利用序列号, 就可以很容易做到去重的效果.

超时的时间如何确定?

Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时 时间都是500ms的整数倍.

如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.

如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推,

以指数形式递增. 累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接.

TCP的 连接管理机制

TCP 是 传输控制协议 是 面向连接的

tcp 为什么要连接?

  • 保证数据的可靠性

TCP 是如何 保证数据可靠性的?

  • 序列号和确认号

  • 三次握手

  • 超时重传

  • 流量控制

  • 拥塞控制

  • 数据校验

  • 数据分段和重组

TCP 三次握手

TCP 三次握手是建立 TCP 连接的过程,确保双方都准备好进行数据传输。

流程

第一次握手:客户端发送 SYN

  • Client (SYN_SENT) ---[SYN]---> Server (SYN_REVD)

客户端状态从 CLOSED 变为 SYN_SENT

客户端向服务器发送一个 SYN(同步序列号)包,表示请求建立连接。

数据包:SYN 包包含一个初始序列号(ISN)。

2. 第二次握手:服务器发送 SYN-ACK

  • Server (SYN_REVD) ---[SYN+ACK]---> Client (SYN_SENT)

服务器状态从 LISTEN 变为 SYN_REVD

服务器收到 SYN 包后,回复一个 SYN-ACK(同步序列号和确认序列号)包,表示同意建立连接,并确认客户端的 SYN 包。

数据包:SYN-ACK 包包含服务器的初始序列号(ISN)和对客户端 SYN 包的确认号(客户端 ISN + 1)。

3. 第三次握手:客户端发送 ACK

  • Client (ESTABLISHED) ---[ACK]---> Server (ESTABLISHED)

客户端状态从 SYN_SENT 变为 ESTABLISHED established 已经建立的

客户端收到 SYN-ACK 包后,再发送一个 ACK(确认序列号)包给服务器,表示确认连接建立。

ACK 包包含对服务器 SYN 包的确认号(服务器 ISN + 1)

为什么要 三次握手 ?

  • 使用最小的成本验证全双工通信信道是通畅的 确保双方都能发送和接收数据。

  • 三次握手可以有效的 防止单机 进行对服务器进行攻击

    通过确认双方的通信意图来防止资源浪费

一次握手行不行?

一次握手不行,因为服务器在收到客户端的 SYN 包后,必须维护一个已经建立好的连接状态,但维护连接是有成本的。如果客户端不发送后续的确认包,服务器会浪费资源等待,容易受到 SYN 洪水攻击(SYN Flood)和分布式拒绝服务攻击(DDoS)。

两次握手行不行?

两次握手也不行,因为无法确保双方都能正确接收到对方的消息。

  1. 第一次握手:客户端发送 SYN 包,服务器收到并回复 SYN-ACK 包。

  2. 第二次握手:客户端收到 SYN-ACK 包后,直接开始发送数据。

在这种情况下,如果服务器的 SYN-ACK 包丢失,客户端会误以为连接已经建立,开始发送数据,而服务器却不知道连接已经建立,导致数据丢失。

四次握手行不行?

四次握手虽然可以建立连接,但会增加额外的开销和复杂性。

三次握手已经足够确保连接的可靠性和双方的同步状态,四次握手没有必要。

!三次握手只是建立连接的形式 不一定必须建立连接成功

! 但是 TCP的连接机制是建立在 TCP 建立连接之后的

虽然三次握手 不一定成功 ,但是 最担心的是最后一个 ACK 丢失

链接 需要被操作系统管理起来 ,但是 维护一个链接是 需要成本的

TCP 四次挥手

TCP 四次挥手是用于关闭连接的过程,确保双方都能正常终止连接

为什么需要四次挥手?

  • 保证数据的完整性:四次挥手确保双方都能正常终止连接,并且所有数据都已成功传输。

  • 防止资源浪费:通过 TIME_WAIT 状态,防止旧的重复数据包影响新连接,确保网络的稳定性。

第一次挥手:客户端发送 FIN

  • 客户端状态从 ESTABLISHED 变为 FIN_WAIT_1

  • Client (ESTABLISHED) ---[FIN]---> Server (CLOSE_WAIT)

客户端向服务器发送一个 FIN(终止连接)包,表示不再发送数据

FIN 包包含当前序列号。

第二次挥手:服务器发送 ACK

  • 服务器状态从 ESTABLISHED 变为 CLOSE_WAIT

  • Server (CLOSE_WAIT) ---[ACK]---> Client (FIN_WAIT_2)

服务器收到 FIN 包后,回复一个 ACK(确认序列号)包,表示确认客户端的 FIN 包。

第三次挥手:服务器发送 FIN

  • 服务器状态从 CLOSE_WAIT 变为 LAST_ACK

  • Server (LAST_ACK) ---[FIN]---> Client (TIME_WAIT)

服务器在发送完所有数据后,向客户端发送一个 FIN 包,表示不再发送数据

FIN 包包含当前序列号

第四次挥手:客户端发送 ACK

  • 客户端状态从 FIN_WAIT_2 变为 TIME_WAIT,然后变为 CLOSED

  • Client (TIME_WAIT) ---[ACK]---> Server (CLOSED)

客户端收到服务器的 FIN 包后,回复一个 ACK 包,表示确认服务器的 FIN 包。

ACK 包包含确认号(服务器 FIN 包的序列号 + 1)

TIME_WAIT 状态

客户端在发送最后一个 ACK 包后进入 TIME_WAIT 状态,持续一段时间(通常为 2 * MSL,最大报文段生存时间)。

MSL 是 是TCP报文的最大生存时间

因此TIME_WAIT持续存在2MSL的话 就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失

  • 保证最哦吼一个 ACK 被对方收到

  • 双方在断开的时候 网络中 还有滞留的报文 保证滞留报文 进行 消散

解决TIME_WAIT状态引起的bind失败的方法

服务器需要处理非常大量的客户端的连接(每个连接的生存时间可能很短, 但是每秒都有很大数量的客户 端来请求).

这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃, 就需要被服务器端主动清理掉), 就会产 生大量TIME_WAIT连接.

由于我们的请求量很大, 就可能导致TIME_WAIT的连接数很多,

每个连接都会占用一个通信五元组(源ip, 源端口, 目的ip, 目的端口, 协议).

其中服务器的ip和端口和协议是固定的.

如果新来的客户端连接的ip和 端口号和TIME_WAIT占用的链接重复了, 就会出现问题.

setsockopt()设置socket描述符的 选项SO_REUSEADDR为1, 表示允许创建端口号相同但IP地址不同的多个 socket描述符

int setsockopt(int sockfd, int level, int optname,
             const void optval[.optlen],
             socklen_t optlen);

int opt = 1;

setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&OPT,sizeof OPT);

CLOSE_WAIT 状态

服务器在收到客户端的 FIN 包后进入 CLOSE_WAIT 状态,等待应用程序处理完所有数据并关闭连接。

服务器在 CLOSE_WAIT 状态下处理剩余的数据传输和资源释放,然后发送 FIN 包关闭连接。

TCP 是 地位对等的协议

主动断开的一方 最终状态是 TIME_WAIT 状态

被动断开的一方 最终状态是 CLOSE_WAIT 状态

TCP 流量控制与滑动窗口

窗口大小

窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。在 TCP 连接建立时,通过三次握手交换窗口大小信息,确定双方的接收能力。

操作系统内核为了维护这个滑动窗口, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答;

只有确认应答过的数据, 才能从缓冲区删掉;

窗口越大, 则网络的吞吐率就越高;

滑动窗口的工作流程

滑动窗口机制用于控制数据流量,确保发送方不会发送超过接收方处理能力的数据

  • win_start = ACK_SEQ:窗口的起始位置是最后一个确认应答的序列号。

  • win_end = win_start + tcp_win:窗口的结束位置是起始位置加上窗口大小。

  • 窗口的移动相当于数组下标的更新

收到应答确认时的处理

当收到应答确认时,如果确认的是中间或结尾的数据段,而不是最左侧发送的报文的确认,窗口仍然需要滑动。

  • 滑动窗口:窗口会根据确认的序列号向后滑动,释放已确认的数据段。

  • 窗口大小的变化:窗口大小会根据接收方的反馈进行调整。接收方通过 ACK 包中的窗口大小字段告知发送方当前可以接收的数据量。如果接收方的缓冲区快满了,窗口大小会变小;如果接收方的缓冲区有足够空间,窗口大小会变大

确认信号的定义

  • ACK seq X+1:表示 X+1 之前的所有数据全部收到了。

重传机制

  • 连续收到 3 个以上相同的 ACK:会触发快速重传机制,发送方会立即重传丢失的数据段。

发送缓冲区的组织

发送缓冲区用于记录当前还有哪些数据没有应答。只有确认应答过的数据,才能从缓冲区中删除。发送缓冲区通常组织为环形结构,以便高效管理数据段。

滑动窗口的空间不足

如果滑动窗口的空间不足,发送方会暂停发送新的数据,直到接收到更多的确认应答,释放出更多的窗口空间。

TCP拥塞控制

  • 上面的 所有的策略基本都是 端到端的

  • 但是丢包的 情况是 复杂的 除了 接收方 出现问题 ,网络也可能出现问题

网络拥塞

TCP 网络实际情况中 会有很多主机 访问同一台服务器 都给服务器发送数据的同时 , 服务器 处理 会花费时间会造成网络拥塞

在不清楚当前网络状态下, 贸然发送大量的数据 指挥加大网络故障

TCP 可靠性 还保证了 网络上的网络问题

TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据;

慢启动

真实的滑动窗口大小 = (拥塞窗口 , 窗口大小(接受能力))

维护一个拥塞窗口(初始为1 ) 每收到一个ACK 将 拥塞窗口+1

拥塞窗口的增长速度 是指数级别的 ,慢启动 是 开始的时候慢 ,但是 增长的时候速度很快

为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍. 此处引入一个叫做慢启动的阈值 当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长

当TCP开始启动的时候, 慢启动阈值等于窗口最大值; 在每次超时重发的时候, 慢启动阈值会变成原来的一半, 同时拥塞窗口置回1

延迟应答

为什么 延迟应答 会优化网络速率 ?

如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小

实际上 计算机处理速度可能很快10ms 就可以更新接收缓冲区 返回一个较大的窗口

窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率

数量限制: 每隔N个包就应答一次; 时间限制: 超过最大延迟时间就应答一次;

的数量和超时时间, 依操作系统不同也有差异; 一般N取2, 超时时间取200ms;

捎带应答

如果接收方在接收到数据包后需要发送数据包,它会将确认应答信息捎带在即将发送的数据包中,而不是单独发送一个确认应答包。这有助于减少数据包的数量,提高传输效率。

粘包问题

粘包问题是指多个数据包在传输过程中被粘在一起,接收方无法区分每个数据包的边界。TCP 没有如同 UDP 一样的 "报文长度" 字段,但有一个序号字段。站在传输层的角度,TCP 是一个一个报文过来的,按照序号排好序放在缓冲区中。站在应用层的角度,看到的只是一串连续的字节数据,这么一连串的字节数据,就不知道从哪个部分开始到哪个部分,是一个完整的应用层数据包。

如何避免粘包问题?

定长包:对于定长的包,保证每次都按固定大小读取即可。

变长包:可以在包头的位置,约定一个包总长度的字段,从而就知道了包的结束位置。

分隔符:在包和包之间使用明确的分隔符(定义在应用层)。

UDP 协议是否存在粘包问题?

UDP 是一个一个把数据交付给应用层,有很明确的数据边界,因此不存在粘包问题。

面向字节流

创建一个TCP的socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区

调用 write 时,数据会先写入发送缓冲区中;如果发送的字节数太长,会被拆分成多个 TCP 的数据包发出;

如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机发送出去。

接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区,然后应用程序可以调用 read 从接收缓冲区拿数据。

TCP 的一个连接既有发送缓冲区,也有接收缓冲区---------------------------全双工。

由于缓冲区的存在, TCP程序的读和写不需要一一匹配

可以调用一次write写100个字节, 也可以调用100次write, 每次写一个字节;

读100个字节数据时, 可以一次read 100个字节, 也可以一次 read一个字节, 重复100次

TCP 异常情况

进程终止:进程终止会释放文件描述符,仍然可以发送 FIN,和正常关闭没有什么区别。

机器掉电/网线断开:接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了,就会进行 reset。即使没有写入操作,TCP 自己也内置了一个保活定时器,会定期询问对方是否还在。如果对方不在,也会把连接释放。另外,应用层的某些协议也有一些这样的检测机制。例如 HTTP 长连接中,也会定期检测对方的状态。

Listen 的 第二个参数

SYNOPSIS
       #include <sys/socket.h>
​
       int listen(int sockfd, int backlog);

backlog 全排列队列

TCP 协议要给上层维护一个 连接队列

  • 等到资源空闲的时候 立马使用

  • 不能没有 要提高资源利用率

  • 队列 不能太长 (连接丢失)

TCP 底层 允许最多 有 backlog + 1 个 完整连接

后续来的都是 半 连接,如果没有 尽快完成握手 自动被server 关掉

TCP小结 为什么TCP这么复杂?

因为要保证可靠性, 同时又尽可能的提高性能.

可靠性: 校验和 序列号(按序到达) 确认应答 超时重发 连接管理 流量控制 拥塞控制

提高性能: 滑动窗口 快速重传 延迟应答 捎带应答

其他: 定时器(超时重传定时器, 保活定时器, TIME_WAIT定时器等

用UDP实现可靠传输(经典面试题)

引入序列号, 保证数据顺序;

引入确认应答, 确保对端收到了数据;

引入超时重传, 如果隔一段时间没有应答, 就重发数据;

Copilot

QUIC 协议

是由 Google 开发的一种基于 UDP 的传输层协议,旨在提供更快、更可靠的互联网连接。QUIC 结合了 TCP 和 TLS 的优点,同时解决了它们的一些缺点。

QUIC 使用 UDP 作为底层传输协议,这使得它能够绕过一些传统 TCP 的限制,如头部阻塞(Head-of-Line Blocking)问题。

QUIC 在传输层集成了 TLS(传输层安全协议),提供了内置的加密和身份验证功能,确保数据传输的安全性。

QUIC 通过减少握手次数,实现了更快的连接建立。通常情况下,QUIC 只需要一次往返(1-RTT)即可建立连接,而传统的 TCP 需要三次握手(3-RTT)。

QUIC 支持多路复用,可以在单个连接上同时传输多个数据流,而不会因为某个数据流的丢包或延迟影响其他数据流。

尽管 QUIC 基于 UDP,但它实现了类似于 TCP 的可靠传输机制,包括序列号、确认应答和重传机制,确保数据的可靠性和顺序性。

QUIC 设计为可扩展协议,允许在不影响现有连接的情况下添加新功能和扩展。

HTTP/1.0

HTTP/1.0 是最早的 HTTP 协议版本,于 1996 年发布。它的主要特点包括:

  • 每个请求/响应都需要建立一个新的 TCP 连接,这导致了大量的连接开销和延迟。

  • 支持基本的 HTTP 方法,如 GET、POST 和 HEAD。

  • 引入了 HTTP 状态码,用于指示请求的成功或失败。

HTTP/1.1

HTTP/1.1 于 1997 年发布,相比 HTTP/1.0 有了显著的改进:

  • 持久连接:允许在同一个 TCP 连接上发送多个请求和响应,减少了连接开销。

  • 管道化:允许在发送前一个响应之前发送多个请求,从而减少了延迟。

  • 缓存控制:引入了更多的缓存控制机制,提高了资源的利用效率。

  • 分块传输编码:支持在响应中分块传输数据,适用于动态生成的内容。

HTTP/2.0

HTTP/2.0 于 2015 年发布,是对 HTTP 协议的一次重大升级:

  • 多路复用:允许在一个 TCP 连接上同时发送多个请求和响应,解决了 HTTP/1.x 的队头阻塞问题。

  • 头部压缩:使用 HPACK 算法对 HTTP 头部进行压缩,减少了传输的数据量。

  • 服务器推送:服务器可以在客户端请求之前主动推送资源,提高了页面加载速度。

HTTP/3.0

HTTP/3.0 是最新的 HTTP 协议版本,于 2022 年发布:

  • 基于 QUIC 协议:HTTP/3.0 使用 QUIC 作为传输层协议,取代了 TCP。

  • 减少延迟:QUIC 协议通过减少连接建立时间和提高数据传输效率,显著降低了延迟。

  • 更好的连接迁移:QUIC 支持在网络切换时保持连接不中断,适用于移动设备。

QUIC 协议

QUIC(Quick UDP Internet Connections)是由 Google 开发的一种传输层协议,旨在替代 TCP:

  • 基于 UDP:QUIC 使用 UDP 作为底层协议,避免了 TCP 的一些固有问题。

  • 快速连接建立:QUIC 结合了连接建立和加密握手,减少了连接建立的往返时间。

  • 多路复用:QUIC 支持在一个连接上同时传输多个数据流,避免了 TCP 的队头阻塞问题。

  • 更好的错误恢复:QUIC 可以更快速地检测和恢复丢失的数据包,提高了传输效率。