深入理解HTTP2 (2)

在一些视频里认为,学习一样东西并记住最好的方法就是自己学习后去教会别人知识。而同样,深入学习一个协议我认为就需要实现它。

b'\x00\x00\x18\x04\x00\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x03\x00\x00\x03\xe8\x00\x04\x00`\x00\x00\x00\x06\x00\x04\x00\x00\x00\x00\x04\x08\x00\x00\x00\x00\x00\x00\xef\x00\x01'
Code language: Python (python)

这是一串由Chrome发送的HTTP2的请求。由于是二进制格式的,所以看起来会毫无秩序,眼花缭乱。

首先让我们看看第一个帧,学习过HTTP2的都会知道,前三个字节包含负载大小。

可知,第一帧负载长度为24。如果你的byteorder选择的是little,就会产生各种问题。
第三,第四字节的内容为4和0,则可知4为帧类型——Setting帧,0为flags,这里并没有设置任何flags。
剩下4个字节就是流ID和R位,第一眼就知道四个字节全都是0,所以流id和R位也都为0.
剩下的24个字节就是剩余帧部分,也就是帧负载,全都是Setting的内容:

解析第一个Setting帧

b'\x00\x01\x00\x01\x00\x00\x00\x03\x00\x00\x03\xe8\x00\x04\x00`\x00\x00\x00\x06\x00\x04\x00\x00'
Code language: JavaScript (javascript)

这是我们得到的第一个帧——Setting帧的帧负载。

由其他人翻译的RFC 7540可知Setting帧格式:

由于帧长度为24,而一个键值对应的参数是32/8+16/8=6个字节,也就是多个参数,因此我们可以使用循环解析:
(中间第一个循环我没有注意到Python可以整除的数结果也会为浮点数,所以…)

我们得到了一个键值对应的内容:

{
    b'\x00\x01': b'\x00\x01\x00\x00', 
    b'\x00\x03': b'\x00\x00\x03\xe8',
    b'\x00\x04': b'\x00`\x00\x00',
    b'\x00\x06': b'\x00\x04\x00\x00'
}Code language: JavaScript (javascript)

可以看到,所有key都是数字——1,3,4,6。

再查询文档可知:

所以单单24个字节,HTTP2已经把上面4个内容给传输给了服务器。

其中比较重要的只有SETTING_HEADER_TABLE_SIZE(1)和SETTINGS_MAX_HEADER_LIST_SIZE(6),流量窗口我们暂时不需要实现(因为默认Chrome的流量窗口已经最大,对于小部分数据如照片,HTML页面等绰绰有余,不需要流量窗口调整大小)。

为什么1和6重要,因为这关乎我们的解码器能否正确的解出被hpack压缩的头部信息,否则我们不知道客户端想要什么,发送过去的数据客户端也无法解析。

下面又来一个重量级问题,如何将字节数据解析为数字???

其实在开头我就已经演示过如何将字节转换为数字。

int.from_bytes(bytes, byteorder, *, signed = False)Code language: PHP (php)

由于几乎所有的HTTP2数据的字节序都是big,所以第二个参数无脑设置”big”即可。signed参数指的是是否有符号,不管他即可。

这样一来我们就能顺利的解析出了第一个帧的内容:

{'1': 65536, '3': 1000, '4': 6291456, '6': 262144}
Code language: JavaScript (javascript)

解析第二个帧

因为第一个帧包括帧头已经占用了24+9=33个字节,因此剩下的帧就是33个字节之后。

继续,前三个字节为帧长度:

只有四个字节,我们再来看看剩下数据的长度:
9字节帧头+4字节帧负载=13字节,因此剩下数据全部都是一个帧。

继续往下解析:

帧类型为Window_Update帧,用于流量窗口控制。
第5个字节即第6-9字节告诉我们没有设置flags,且这个帧属于流0且还是没有设置R标志。

可能你们看我已经提过两次流0和R标志,有点疑惑——四个字节里面哪一个是R标志哪一个是流标识?

这就需要深入理解字节与比特的关系:

一个字节由8个比特组成,而比特只能为0或1。所以32个比特就是32/8=4个字节。

而R标志只占用这4个字节中的第一个比特:

(mspaint随手作图,臭勿喷)

如图,从第一位到第八位就是一个字节,其中R标志只占用一个比特,所以R标志只有0和1两种形态。至于R标志是什么,文档上说他是个保留位。在Header帧中为E单位…

所以你需要拆分一个字节使其成为8个0/1的组合。

根据这个函数,我们可以获取第一个比特,其就为0。R标志一般为空(即0)。

最后,解析最后四个字节转化为数字:

这个帧告诉了服务器当前流量窗口为15663105字节,为Chrome设置的初始流量窗口,对于流量窗口,下一*n篇文章再详细讲解。文档看得可能比较生硬。

至此,Chrome发送的第一段内容包括两个帧我们已经解析完毕,下一节可能会讲解流量窗口和Header帧的解析。

由于高中开学了,每周只有周末能回家,更新较慢我较懒博客较冷清。

等待更新

文档引用:

HTTP/2 中的帧定义 (halfrost.com)

发表回复