strongcode9527/blog

http2

Opened this issue · 8 comments

http/2 学习

自己对于http/2 的理解还停留在面试死记硬背的水平,所以最近对自己心中一些疑问以及自己的理解做一个总结,这些总结可能会有错误。如果有大佬觉得哪里有什么问题,可以直接说出。

http/2 的完整介绍很多。珠玉在前:http/2 简介 ,就不在赘述了。

问题一: http/2 为什么要使用二进制分帧层

二进制协议的理解:

对于这个问题,我们首先就要搞清楚什么http1.1的文本协议和htt/p2的二进制协议的不同了。其实我作为一个基础并不牢固的搬砖工,当看到这个二进制的时候我就在想:在计算机世界当中所有的内容不都是二进制吗?为什么还要有二进制以及文本协议之分呢?

带着这个问题,我在网上找到了一些回复,就直接搬下来吧

Binary protocol versus text protocol isn't really about how binary blobs are encoded. The difference is really whether the protocol is oriented around data structures or around text strings. Let me give an example: HTTP. HTTP is a text protocol, even though when it sends a jpeg image, it just sends the raw bytes, not a text encoding of them.

四级低空略过水平的翻译:

二进制协议和文本协议的区别并不是关于二进制blob是如何编码的。关键在于协议是以数据结构为导向还是以文本字符串为导向。

文本协议请求:其中所有的内容都是文字

二进制请求:

struct request {
  int requestType;
  int protocolVersion;
  char path[1024];
  char user_agent[1024];
  char host[1024];
  long int accept_bitmask;
  long int language_bitmask;
  long int charset_bitmask;
};

当我们看到http文本协议以及二进制请求的结构之后:

  • 二进制协议比文本协议更加紧凑。
  • 文本协议解析是需要特殊字符的,比如\r\n,一方面造成了文本协议更加冗长,另一方面使用这种特殊字符解析一段文字的方式,没有二进制解析方便快捷。
  • 二进制协议中的数字比文本中的数字占用空间更小:比如http中状态码200,在文本协议中是 "2" "0" "0",这三个数字要分别用编码表示的,但是在二进制协议中,不用将数字编码成文本格式,直接用二进制表示数字就可以了: 11001000 。

http/2中的二进制分帧层

参考文章

At the core of all performance enhancements of HTTP/2 is the new binary framing layer, which dictates how the HTTP messages are encapsulated and transferred between the client and server.

_
在许多文章中,都说http/2中的二进制分帧层,是http/2性能提升的关键,它给一个tcp链接同时发送多个请求提供了可能。

http.png

HTTP/1.1和HTTP/2都是基于TCP的协议,TCP模型是双向数据流,任何在一个TCP连接上处理超过一个请求的协议都需要解决这样两个问题:

  1. 分片——如何将流中的多个请求和响应拆分成独立的消息
  2. 对应——如何将请求和响应对应起来

在这两个问题中,我觉得第二个往往会让人忽略,就是在http1.1 中,一个tcp请求只能同时完成一个请求响应过程,所以请求以及响应天生一一对应,不存在响应和请求无法匹配的问题。但是当我们打开抓包工具查看相应的相关http报文的时候,会发现其实响应是没有一个字段去和请求一一对应的。这就给http/2的多路复用带来了问题,一个tcp连接中多个请求,多个响应,那么如何将这些响应和请求一一对应呢?只能在二进制分帧层添加字段了,所以在http/2帧的报文格式中,有一个字段是Stream Identifier,这个流ID可以将响应和请求一一对应。这个根本解决了多路复用,请求以及响应匹配的问题。

所以给这个问题来一个总结吧:

  • 二进制协议比文本协议更加紧凑,减少占用空间。
  • 分帧层相当于将http切分,更加灵活,比如可以对header帧做单独的特殊处理。
  • 分帧层有着属于自己的报文头,其中的Stream Identifier 使得操作系统具备将多个响应以及请求一一匹配的能力,这个是http/2 性能提升的关键,也就是多路复用。
  • 二进制分帧层存在的意义是将请求或响应切分,可以更加灵活处理。分帧层定义的报文格式,例如Stream Identifier等,直接关乎http/2 的各种优化方案的实现。

问题二:tcp与http/2的帧的关系

在http/2中帧是最小通信单位,我的问题是http/2 中的帧和最小的tcp数据包是什么关系,一一对应的吗?在http/2 的请求中,tcp是如何拆分以及组装消息的?

很多面试题中多路复用的解释

其中的解释如下:

  • 同域名下所有通信都在单个连接上完成,消除了因多个 TCP 连接而带来的延时和内存消耗。
  • 单个连接上可以并行交错的请求和响应,之间互不干扰

下面的解释当中,并行交错的请求和响应。其实这个并行请求应该具体为并行http请求,那么tcp可以将请求拆分并行请求吗?

这个就要从tcp如何组装以分拆http报文说起了,

TCP 数据包在 IP 数据包的负载里面。它的头信息最少也需要20字节,因此 TCP 数据包的最大负载是 1480 - 20 = 1460 字节。由于 IP 和 TCP 协议往往有额外的头信息,所以 TCP 负载实际为1400字节左右。因此,一条1500字节的信息需要两个 TCP 数据包

我们假设有两个post请求并行进行请求:

截屏2021-02-27 上午10.32.35.png

如果TCP协议层面并行请求,并将顺序打乱,这样可以吗?
截屏2021-02-27 上午10.22.02.png

在我看来其实这是不可以的,因为tcp协议是完全按照顺序组装数据包的,如果并行请求并将数据包顺序打乱,那么计算机将不知道如何组装这些数据包。

所以上面的并行请求是不准确的,在计算机内部一定是串行请求,发完一个请求的数据包,在发送下一个请求的数据包,这样才能保证数据包组合正确。

所以http/2中的帧,是按照顺序串行请求的,如果不是这样,数据将无从组装,一一对应。
截屏2021-02-27 上午10.30.21.png

总结一下吧:

  • http/2中的帧是最小通信单位,但这仅仅是在http/2这一协议层。一个http/2中的帧,如果信息过多,可能是由多个tcp拆分组成,所以http/2中的帧概念,并不是一一对应网络协议中的一个数据包。
  • 一个或多个tcp报文组成一个帧,一个或多个帧组成一个http/2的请求或者响应。
  • http/2 一个tcp链接不存在并行传输,是串行传输,发送完一个帧的数据包,再发送下一个帧的相关数据包。

如果有哪些地方理解不到位,请不吝赐教

受益良多,解决了我很多疑惑,非常感谢

我感觉二进制分帧,一块是二进制、一块是分帧,起重要作用的是分帧,二进制这块其实提升不高

最近也在学这方面的知识,正好看见了这篇笔记,写的挺好的,在结尾部分有些概念,感觉可能需要辨析一下。
1.不同层数据单位:
应用层:消息/报文
运输层:tcp数据段/udp数据报
ip层:数据包/报文分组
链路层:帧
物理层:字节流

2.http是应用层,所以我们可以用消息/报文来描述,因为应用层发送消息可能会超出最大报文段长度要求,所以需要把应用层消息拆开发送,如果是http1.1版本的,这条tcp通道虽然支持复用,但不支持多个http并发使用(虽然有流水线模式,但http1.1中兼容的不好),tcp通过滑动窗口+选择重传机制保证了应用层消息的稳定性和顺序性,所以在http1.1版本中的消息不乱序,其实是由tcp层保证的,因为一个tcp在一次应用层发送消息请求中,不管消息被分成多少段,这些段都是属于一次消息发送请求的,这个消息在服务端分好段,这些段编好序发送后,客户端的tcp会保证这些段按发送序拼接,并将完整报文提交应用层。这样客户端使用该端口的应用程序就可以成功获取服务端发送的消息。

3.由2中,可以了解到,http1.1中,一个消息不乱序是由tcp+同一时刻就一个应用层请求使用tcp通道实现的。但我们在日常使用中,比如打开一个网页,输入网址后,做dns解析,并请求服务器,忽略过程中的路由和链路等细节,简单来说就是客户端和服务端会建立tcp连接,即做三次握手,如果是https,就再加一层ssl/tsl(也可以简单理解成安全层的握手),建立了端到端的连接以后,就可以开始应用层通信了。客户端发送给网页的http请求后,网页会返回该页面的html等,客户端除了需要网页的基本html,也会需要涉及浏览器做页面渲染需要的图片和css/js等文件。如下图,请求百度后,还请求了一堆资源文件。如果http不支持并发使用tcp,那在同一个域名下的http请求就需要串行请求,必须等到客户端和服务端做完一次http交互后,下一个http请求才能用这个tcp连接,这就使得本来是支持并发的信道强行被改成了串行,就非常浪费资源。http1.1中,是通过对同一个域名建多个tcp,并复用这些tcp连接来解决这个问题的,这显然是一个过渡方案。
image

4.由2和3中可以知道,http1.1前期发展,因为依赖tcp来保证http消息不乱序,不好扩展,所以在http2.0中,就需要把这个顺序的控制由依赖tcp改成不依赖tcp,以前应用层消息如果要分段发送,应用层自己本身压根就没有设置什么标志位去记录,一个消息就是应用层数据的最小单位,分段放到tcp里面发送,完全由tcp控制。那现在的思路就是加一层二进制分帧层,应用层消息的最小单位不再是消息文本了,而是应用层帧,即在应用层就切好数据,并标记好这些帧是属于哪一个http请求的第几节的流,然后再给tcp送出去,这样即使多个http请求并发发送,客户端都可以在应用层自己基于这些标记来把应用层的帧数据拼接成原始消息。

以上就是我关于题主总结的论述,所以提几个不同想法:
1.http/2中的帧概念,是一一对应网络协议中的一个数据包(因为应用层自己分,自己拼)
2.一个tcp报文就是一个帧,一个或多个帧组成一个http/2的请求或者响应
3.http/2 一个tcp连接是并行传输的

最近也在学这方面的知识,正好看见了这篇笔记,写的挺好的,在结尾部分有些概念,感觉可能需要辨析一下。 1.不同层数据单位: 应用层:消息/报文 运输层:tcp数据段/udp数据报 ip层:数据包/报文分组 链路层:帧 物理层:字节流

2.http是应用层,所以我们可以用消息/报文来描述,因为应用层发送消息可能会超出最大报文段长度要求,所以需要把应用层消息拆开发送,如果是http1.1版本的,这条tcp通道虽然支持复用,但不支持多个http并发使用(虽然有流水线模式,但http1.1中兼容的不好),tcp通过滑动窗口+选择重传机制保证了应用层消息的稳定性和顺序性,所以在http1.1版本中的消息不乱序,其实是由tcp层保证的,因为一个tcp在一次应用层发送消息请求中,不管消息被分成多少段,这些段都是属于一次消息发送请求的,这个消息在服务端分好段,这些段编好序发送后,客户端的tcp会保证这些段按发送序拼接,并将完整报文提交应用层。这样客户端使用该端口的应用程序就可以成功获取服务端发送的消息。

3.由2中,可以了解到,http1.1中,一个消息不乱序是由tcp+同一时刻就一个应用层请求使用tcp通道实现的。但我们在日常使用中,比如打开一个网页,输入网址后,做dns解析,并请求服务器,忽略过程中的路由和链路等细节,简单来说就是客户端和服务端会建立tcp连接,即做三次握手,如果是https,就再加一层ssl/tsl(也可以简单理解成安全层的握手),建立了端到端的连接以后,就可以开始应用层通信了。客户端发送给网页的http请求后,网页会返回该页面的html等,客户端除了需要网页的基本html,也会需要涉及浏览器做页面渲染需要的图片和css/js等文件。如下图,请求百度后,还请求了一堆资源文件。如果http不支持并发使用tcp,那在同一个域名下的http请求就需要串行请求,必须等到客户端和服务端做完一次http交互后,下一个http请求才能用这个tcp连接,这就使得本来是支持并发的信道强行被改成了串行,就非常浪费资源。http1.1中,是通过对同一个域名建多个tcp,并复用这些tcp连接来解决这个问题的,这显然是一个过渡方案。 image

4.由2和3中可以知道,http1.1前期发展,因为依赖tcp来保证http消息不乱序,不好扩展,所以在http2.0中,就需要把这个顺序的控制由依赖tcp改成不依赖tcp,以前应用层消息如果要分段发送,应用层自己本身压根就没有设置什么标志位去记录,一个消息就是应用层数据的最小单位,分段放到tcp里面发送,完全由tcp控制。那现在的思路就是加一层二进制分帧层,应用层消息的最小单位不再是消息文本了,而是应用层帧,即在应用层就切好数据,并标记好这些帧是属于哪一个http请求的第几节的流,然后再给tcp送出去,这样即使多个http请求并发发送,客户端都可以在应用层自己基于这些标记来把应用层的帧数据拼接成原始消息。

以上就是我关于题主总结的论述,所以提几个不同想法: 1.http/2中的帧概念,是一一对应网络协议中的一个数据包(因为应用层自己分,自己拼) 2.一个tcp报文就是一个帧,一个或多个帧组成一个http/2的请求或者响应 3.http/2 一个tcp连接是并行传输的

我觉得 http 1.1 的版本只是在一个 tcp 连接中只能处理一个请求,来确保请求与响应的一一对应。到了 http2,一个 tcp 链接对应多个请求,就需要在 http 应用层新添加一个字段标识是是属于哪个请求。

我感觉二进制分帧,一块是二进制、一块是分帧,起重要作用的是分帧,二进制这块其实提升不高

二进制的概念在头部分帧中,会进行头部数据的压缩,这个对于请求体的体积较少特别明显。如果还是之前的文本格式,则无法进行头部压缩

最近也在学这方面的知识,正好看见了这篇笔记,写的挺好的,在结尾部分有些概念,感觉可能需要辨析一下。 1.不同层数据单位: 应用层:消息/报文 运输层:tcp数据段/udp数据报 ip层:数据包/报文分组 链路层:帧 物理层:字节流

2.http是应用层,所以我们可以用消息/报文来描述,因为应用层发送消息可能会超出最大报文段长度要求,所以需要把应用层消息拆开发送,如果是http1.1版本的,这条tcp通道虽然支持复用,但不支持多个http并发使用(虽然有流水线模式,但http1.1中兼容的不好),tcp通过滑动窗口+选择重传机制保证了应用层消息的稳定性和顺序性,所以在http1.1版本中的消息不乱序,其实是由tcp层保证的,因为一个tcp在一次应用层发送消息请求中,不管消息被分成多少段,这些段都是属于一次消息发送请求的,这个消息在服务端分好段,这些段编好序发送后,客户端的tcp会保证这些段按发送序拼接,并将完整报文提交应用层。这样客户端使用该端口的应用程序就可以成功获取服务端发送的消息。

3.由2中,可以了解到,http1.1中,一个消息不乱序是由tcp+同一时刻就一个应用层请求使用tcp通道实现的。但我们在日常使用中,比如打开一个网页,输入网址后,做dns解析,并请求服务器,忽略过程中的路由和链路等细节,简单来说就是客户端和服务端会建立tcp连接,即做三次握手,如果是https,就再加一层ssl/tsl(也可以简单理解成安全层的握手),建立了端到端的连接以后,就可以开始应用层通信了。客户端发送给网页的http请求后,网页会返回该页面的html等,客户端除了需要网页的基本html,也会需要涉及浏览器做页面渲染需要的图片和css/js等文件。如下图,请求百度后,还请求了一堆资源文件。如果http不支持并发使用tcp,那在同一个域名下的http请求就需要串行请求,必须等到客户端和服务端做完一次http交互后,下一个http请求才能用这个tcp连接,这就使得本来是支持并发的信道强行被改成了串行,就非常浪费资源。http1.1中,是通过对同一个域名建多个tcp,并复用这些tcp连接来解决这个问题的,这显然是一个过渡方案。 image

4.由2和3中可以知道,http1.1前期发展,因为依赖tcp来保证http消息不乱序,不好扩展,所以在http2.0中,就需要把这个顺序的控制由依赖tcp改成不依赖tcp,以前应用层消息如果要分段发送,应用层自己本身压根就没有设置什么标志位去记录,一个消息就是应用层数据的最小单位,分段放到tcp里面发送,完全由tcp控制。那现在的思路就是加一层二进制分帧层,应用层消息的最小单位不再是消息文本了,而是应用层帧,即在应用层就切好数据,并标记好这些帧是属于哪一个http请求的第几节的流,然后再给tcp送出去,这样即使多个http请求并发发送,客户端都可以在应用层自己基于这些标记来把应用层的帧数据拼接成原始消息。

以上就是我关于题主总结的论述,所以提几个不同想法: 1.http/2中的帧概念,是一一对应网络协议中的一个数据包(因为应用层自己分,自己拼) 2.一个tcp报文就是一个帧,一个或多个帧组成一个http/2的请求或者响应 3.http/2 一个tcp连接是并行传输的

那为什么不直接在消息中增加流ID这个标识?

TCP 报文(数据包):

TCP 报文是传输层级别的数据单元。它负责将数据可靠地从一个网络节点传输到另一个节点。
TCP 通过将数据切割成报文(或称为数据包)来进行传输。
TCP 报文的发送是有序的,并且 TCP 协议保证了数据的顺序性和可靠性。

HTTP/2 帧:

HTTP/2 帧是应用层级别的数据单元,是 HTTP/2 通信中的最小数据单位。
HTTP/2 协议通过在一个 TCP 连接上发送多个帧来实现多路复用。这些帧可以属于不同的 HTTP/2 流。

问题

一个或多个 TCP 报文组成一个帧?

通常情况下,一个 HTTP/2 帧可以被封装进一个或多个 TCP 报文中进行传输。由于 TCP 和 HTTP/2 操作在不同的层级,一个大的 HTTP/2 帧可能需要分割成多个 TCP 报文来传输,反之亦然。

一个 TCP 报文就是发送一个帧?

不一定。一个 TCP 报文可能只包含了一个 HTTP/2 帧的一部分,也可能包含了一个完整的帧,甚至多个帧。这取决于帧的大小和 TCP 报文的最大传输单元(MTU)。

TCP 报文的发送是顺序的吗?

是的,TCP 报文的发送是顺序的。TCP 保证数据按照发送顺序到达接收方。如果某个数据包丢失,TCP 会重传这个包,直到它被正确接收。

为什么说允许在单一的 TCP 连接上并行发送多个 HTTP/2 帧?

在 HTTP/2 中,多路复用允许在同一个 TCP 连接上同时传输属于不同 HTTP/2 流的多个帧。这些帧可以交错发送,即在一个流的帧被完全传输之前,可以开始传输另一个流的帧。这种方式允许并行处理多个请求和响应,减少了延迟,并提高了连接的利用率。这并不违反 TCP 的顺序性原则,因为 TCP 层只负责传输封装了 HTTP/2 帧的 TCP 报文,而不关心报文内部的具体内容如何组织。

最近同时学了这方面的知识,正好看到了这篇笔记,写了概述的,在结尾部分一些概念,感觉可能需要辨析一下。 1.不同层数据单位: 应用层:消息/报文 运输层: tcp数据段/udp数据报 ip层:数据包/报文包 仓库层:帧 物理层:字节流

2.http是应用层,所以我们可以用消息/报文来描述,因为应用层发送消息可能会超出最大报文段长度要求,所以需要把应用层消息拆开发送,如果是http1.1版本的,这条tcp通道虽然支持复用,但不支持多个http并发使用(虽然有模拟模式,但http1.1中兼容的不好),tcp通过滑动窗口+选择重传机制保证了应用层消息的稳定性和顺序性,所以在http1.1版本中的消息不乱序,其实是由tcp层保证的,因为一个tcp在一次应用层发送消息请求中,不管消息被拆分多少段,这些段都是一次消息发送请求的,这个消息在服务端分好段,这些段编好序发送后,客户端的tcp会保证这些段按发送序拼接,把完整的报文提交应用层属于该客户端使用。端口的应用程序就可以成功获取服务端发送的消息。

3.由2中,可以清楚,http1.1中,一个消息不乱序是由tcp+同一个应用层请求使用tcp通道实现的。但时刻我们在日常使用中,比如打开一个网页,输入网址之后,做dns解析,并请求服务器,忽略过程中的路由和流量等细节,简单来说就是客户端和服务端会建立tcp连接,即做三次握手,如果是https,就加上一层ssl /tsl(也可以简单理解成安全层的握手),建立了高精度的连接以后,就可以开始应用层通信了。客户端发送给网页的http请求后,网页会返回该页面的html等,客户端除了需要网页的基本html,还需要涉及浏览器做页面渲染需要的图片和css/js等文件。如下图,请求百度后,还请求了一批资源文件。如果http不支持使用tcp ,那在同一个域名下的http请求就需要串行请求,必须等到客户端和服务端做完一次http交互后,下一个http请求才能用这个tcp连接,这就使得本来就是支持字符串的信道强行被改成串口,就非常浪费资源。http1.1中,是通过对同一个域名建多个tcp,并复用这些tcp连接来解决这个问题的,这显然是一个过渡方案。 图像

4.由2和3中可以知道,http1.1前期发展,因为依赖tcp来保证http消息不乱序,不好扩展,所以在http2.0中,就需要把这个顺序的控制由依赖tcp改成不依赖tcp,之前应用层消息如果要分段发送,应用层本身压根就完全没有设置什么标志位去记录,一个消息就是应用层的最小单位,分段放在tcp里面发送,由tcp控制那现在的思路就是加一层二进制分帧层,应用层消息的最小单位不再是消息文本了,而是应用层帧,即在应用层就切好的数据,并标记好这些帧是属于哪一个一个http请求的第几节流,然后再给tcp发送出去,这样即使多个http请求并发发送,客户端都可以在应用层自己基于这些标记来把应用层的帧数据拼接成原始消息。

以上就是我关于题主总结的论述,所以提几个不同的想法: 1.http/2中的帧概念,是一个对应网络协议中的一个数据包因为(应用层自己分,自己拼) 2.一个tcp报文就是一个帧,一个或多个帧组成一个http/2的请求或者响应 3.http/2 一个tcp连接是CPU传输的

没太懂,4里面说【并标记好这些帧是属于哪一个一个http请求的第几节】,意思是一个tcp数据包是一个请求的,但是多个请求的数据包可以打乱,但是按这个说法【因为tcp协议是完全按照顺序拼凑数据包的】,那根据tcp拼出来的数据不是杂糅了多个http的包,那如果要用帧里面标识的属于哪个http请求,那tcp就不能拼凑数据包了,要在http上去拼凑,这样理解对吗。那可以让tcp不去拼合数据包吗