前言
在前一篇的博客中,当客户端发送SQL请求到Server端时,产生了一系列Packet。第一个Packet就是本篇要介绍的HandShake。不了解的可以点击上篇博客。
Handshake Packet格式
Handshake packet是由Server向Client发送的初始化包,因为所有从Server向Client端发送的包都是一样的格式,前面的四个字节是包头,其中前三位代表Handshake packet数据长度,第四位是包序列号。下面是Handshake packet具体内容的格式:
相对包内容的位置 | 长度(字节) | 名称 | 描述 |
---|---|---|---|
0 | 1 | 协议版本 | 协议版本的版本号,通常为10(0x0A) |
1 | len = strlen (server_version) + 1 | 数据库版本 | 长度为数据库版本字符串的长度+上标示结束的的一个字节 |
len + 1 | 4 | 线程ID | 此次连接MySQL Server启动的线程ID |
len + 5 | 8 + 1(0x00表示结束) | 挑战随机数(第一部分) | 用于后续账户密码验证 |
len + 14 | 2 | 数据库提供的功能 | 用于与客户端协商通讯方式 |
len + 16 | 1 | 编码格式 | 标识数据库目前的编码方式 |
len + 17 | 2 | 服务器状态 | 用于表示服务器状态,比如是否是事务模式或者自动提交模式 |
len + 19 | 2 | 扩展的协议,数据库提供的功能 | 用于与客户端协商通讯方式 |
len + 21 | 1 | Authentication Plugin length | 身份验证插件长度 |
len + 22 | 10 | 保留字节 | 未来可能会用到,预留字节 |
len + 32 | 12 + 1(0x00表示结束) | 挑战随机数(第二部分) | 用于后续账户密码验证 |
在网上查的资料当时保留字节是13位,而我用的MySQL是8.x的,观察我捕获到的包做了点修改,即len+19和len+21位的使用保留字节,所以现在只剩下10个预留字节了。
WireShark分析HandShake Packet
上面的数据格式的描述可能不够具体,所以这里用WireShark来捕获Handshake看看数据是否真如上面所描述的。
点击Server Greeting proto=10 version=8.0.12
可以看到从Frame
物理层到MySQL Protocol
应用层各层的具体内容。点击MySQL Protocol
可以看到具体的十六进制的内容,以及它的ASCII码。
其中Packet Length
和Packet Number
就是包的数据头,从上面我们知道,Packet Length
是占三个字节的。那么就是说0x00004a
表示的就是数据长度,转为十进制发现就是74。
这里有人可能会有疑惑,为什么不是0x4a0000
这是因为MySQL采用的是小端存储形式。为什么采用这个形式,可以看最后一段过程中遇到问题的说明。
这里再解释一下Version
这个字段,根据上面的描述,我们知道它的长度为数据库版本字符串的长度+上标示结束的一个字节。就是说实际上存储的时候还有一个0x00
的字节跟着version
字段表示结束符。你会发现有好多这种字段跟上结束符的数据,所以在填充数据的时候,需要把0x00
也一起填上。
有了上面两个字段的解释,我相信每个字段的数据你们都能对应上去了,所以这里就不解释了。
过程中遇到的问题
- 为什么会有大小端?为什么采用小端计算机计算会更加方便?
你算加减乘的时候是从高位还是低位开始的?
为什么不从高位开始?因为存在着进位,如果从高位开始,算到后面发现有进位,就要回退到高位处理进位
如果从低位开始,可以一下子算出来。早期的计算机是纸带机,纸带上储存的是2进制数据如果使用小端储存,做加减乘运算时,计算机的写出头可以一直往一个方向移动,而不用回退写出头处理进位问题。
- 既然小端像上面所说的一样有计算方便的优势,为什么还要出现大端呢?
大端也有自己的优势,在判定正负时,符号位固定为第一个字节,容易判断。