在一些视频里认为,学习一样东西并记住最好的方法就是自己学习后去教会别人知识。而同样,深入学习一个协议我认为就需要实现它。
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的都会知道,前三个字节包含负载大小。
解析第一个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帧格式:
我们得到了一个键值对应的内容:
{
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)
解析第二个帧
继续,前三个字节为帧长度:
继续往下解析:
可能你们看我已经提过两次流0和R标志,有点疑惑——四个字节里面哪一个是R标志哪一个是流标识?
这就需要深入理解字节与比特的关系:
一个字节由8个比特组成,而比特只能为0或1。所以32个比特就是32/8=4个字节。
而R标志只占用这4个字节中的第一个比特:
(mspaint随手作图,臭勿喷)
如图,从第一位到第八位就是一个字节,其中R标志只占用一个比特,所以R标志只有0和1两种形态。至于R标志是什么,文档上说他是个保留位。在Header帧中为E单位…
所以你需要拆分一个字节使其成为8个0/1的组合。
最后,解析最后四个字节转化为数字:
至此,Chrome发送的第一段内容包括两个帧我们已经解析完毕,下一节可能会讲解流量窗口和Header帧的解析。
由于高中开学了,每周只有周末能回家,更新较慢我较懒博客较冷清。
等待更新
文档引用: