前言
这篇主要用代码去实现,如何将Server端反馈回来的包,进行解析。如下图,完成三次握手之后,服务端发送了一个handshake packet。我们要做的是定义好handshake的类,以及实现将字节到类的转化。
Handshake类的实现
简单的看一下Handshake类的成员
1 | public class HandshakePacket extends MysqlPacket { |
MysqlPacket是所有包的基础类,含有包的长度以及包的序列号。read和write是其两个抽象方法。这篇主要是讲解Packet如何根据数据类型来实现read。
1 | public abstract class MysqlPacket { |
Packet Read实现的思路
首先来看packetLength是如何获取的。在前面的博客中我们得知,该数据是占用三个字节的。所以有下面的方法:
1 | /** |
同理像packetId、protocolVersion只用一个字节就可以存放的,就可以用下面的方法:
1 | public byte read() { |
有一个要注意的是如果某个字段占用较大的字节数,如threadID该字段需要占用了四个字节。如果用int来存储就会有问题。因为已经表明了该字段需要四个字节,虽然int是四个字节,但是其最高位是表示符号的。所以int来放最多只能表示3个字节+7位。
所以为了溢出需要用long来存储。
1 | public long readUB4() { |
特殊的字段还有salt,根据它存储格式描述来看,它是以null为结尾的数据,所以实现如下:
1 | public byte[] readBytesWithNull() { |
特殊的字段还有Auth Packet中的password,其数据格式为LengthEncodedString,即在password之前有一个表示该字段的长度。
使用LengthEncodedInteger编码的整数可能会使用1, 3, 4, 或者9 个字节,具体使用字节取决于数值的大小,下表是不同的数据长度的整数所使用的字节数:
最小值(包含) | 最大值(不包含) | 存储方式 |
---|---|---|
0 | 251 | 1个字节 |
251 | 2^16 | 3个字节(0xFC + 2个字节具体数据) |
2^16 | 2^24 | 4个字节(0xFD + 3个字节具体数据) |
2^24 | 2^64 | 9个字节(0xFE + 8个字节具体数据) |
其read方法如下:
1 | public byte[] readBytesWithLength() { |
还有如Auth packet中的Username是一个NullTerminatedString格式的数据,其read方法如下:
1 | public String readStringWithNull() {//实现和readBytesWithNUll其实是类似的 |