12 协议解析 字段类型分析

该协议具有一些在整个协议中使用的非常基本的类型:

  • 整数类型
  • 字符串

Integer Types

MySQL协议具有一组可编码的整型:

  • 定长整数类型
  • 长度可编码整数类型

Protocol::FixedLengthInteger

定长整数类型。固定长度的无符号整数将其值存储在一系列字节中,最低有效字节在前(小端存储模式)。最低有效位是指删除后对整个值影响最小的那一位。

通过网络发送这字节时,接收方需要知道字节内容是如何编码的(小端存储模式/大端存储模式)才能将原始字节转换为内部整数表示形式。

  • 小端存储模式:较的有效字节存放在较的存储器地址,较的有效字节存放在较的存储器地址。(即正常的高位在左的模式,有利于计算机处理,因为计算机都是从低位开始往高位处理的)
  • 大端存储模式:较的有效字节存放在较的存储器地址,较的有效字节存放在较的存储器地址。(符合正常的人类思维,人类都是从高位开始往低位阅读的)

大小端模式与硬件的体系结构相关

MySQL使用以下固定长度的无符号整数变体:

  • int<1>: 1 byte
  • int<2>: 2 byte
  • int<3>: 3 byte
  • int<4>: 4 byte
  • int<6>: 6 byte
  • int<8>: 8 byte

示例

一个长度为3的定长整数类型,内容值为1,存储形式如下:01 00 00

Protocol::LengthEncodedInteger

int<lenenc>长度可编码整数类型。使用1、3、4或9个字节的整数,具体取决于其数字值。

将数字值转换为长度可编码整数:

大于/等于小于存储为
02511-byte integer
2512^160xFC + 2-byte integer
2162^240xFD + 3-byte integer
2242^640xFE + 8-byte integer

要将长度可编码整数转换为数字值,要检查第一个字节。

  • 如果它小于0xfb,则将其视为1字节整数。
  • 如果为0xfc,则后跟2个字节的整数。
  • 如果为0xfd,则后跟3个字节的整数。
  • 如果为0xfe,则后跟8个字节的整数。

警告:如果数据包的第一个字节是长度可编码整数,并且其字节值为0xFE,则必须检查数据包的长度以验证其是否有足够的空间用于8字节整数。如果不是,则可能是EOF_Packet

根据上下文,第一个字节可能还具有其他含义:

  • 如果为0xfb,则在ProtocolText::ResultsetRow中表示NULL。
  • 如果为0xff,则是ERR_Packet的第一个字节。

警告:未定义0xff作为长度可编码整数类型的第一个字节。

示例

  • fa小于0xfb,表示一个字节,它的十进制值为250
  • fc fb 00的第一个字节是0xfc,表示长度是两个字节的整数,它的值是fb 00即十进制值为251

String Types

字符串是字节序列,在协议中以多种形式出现。

Protocol::FixedLengthString

string<fix>:固定长度的字符串具有已知的硬编码长度。

示例

ERR_Packet长度始终为5个字节。

Protocol::NullTerminatedString

string<NUL>:以00字节结尾的字符串。

Protocol::VariableLengthString

string<var>:字符串的长度由另一个字段确定或在运行时计算出来。

Protocol::LengthEncodedString

string<lenenc>:长度可编码字符串是一个以描述字符串长度的长度编码整数类型作为前缀的字符串,其实是上面一种类型的特例。

字段

  • length(int<lenenc>):字符串的长度
  • string(string<fix>):[len=$length]string

Protocol::RestOfPacketString

string<EOF>:如果字符串是数据包的最后一个组成部分,则可以从总数据包长度减去当前位置来计算其长度。

Describing Packets

首先定义每个数据包的payload来描述每个数据包,并提供一个示例,显示每个发送的数据包,包括其数据包头:

<packetname>
  <description>

  direction: client -> server
  response: <response>

  payload:
    <type>        <description>

  Example:
    01 00 00 00 01

其中<type>描述数据包字节的顺序:

TypeDescription
int<1>1 byte Protocol::FixedLengthInteger
int<2>2 byte Protocol::FixedLengthInteger
int<3>3 byte Protocol::FixedLengthInteger
int<4>4 byte Protocol::FixedLengthInteger
int<6>6 byte Protocol::FixedLengthInteger
int<8>8 byte Protocol::FixedLengthInteger
int<lenenc>Protocol::LengthEncodedInteger
string<lenenc>Protocol::LengthEncodedString
string<fix>Protocol::FixedLengthString
string<var>Protocol::VariableLengthString
string<EOF>Protocol::RestOfPacketString
string<NUL>Protocol::NulTerminatedString

注意:某些数据包具有可选字段或不同的字段分布,具体取决于作为Protocol::HandshakeResponse数据包一部分发送的Protocol::CapabilityFlags

如果字段具有固定值,则在description部分将其显示为十六进制值,如方括号:[00]。

Binary Protocol Value

下面这一批都是类似的

ProtocolBinary::MYSQL_TYPE_STRING ProtocolBinary::MYSQL_TYPE_VARCHAR ProtocolBinary::MYSQL_TYPE_VAR_STRING ProtocolBinary::MYSQL_TYPE_ENUM ProtocolBinary::MYSQL_TYPE_SET ProtocolBinary::MYSQL_TYPE_LONG_BLOB ProtocolBinary::MYSQL_TYPE_MEDIUM_BLOB ProtocolBinary::MYSQL_TYPE_BLOB ProtocolBinary::MYSQL_TYPE_TINY_BLOB ProtocolBinary::MYSQL_TYPE_GEOMETRY ProtocolBinary::MYSQL_TYPE_BIT ProtocolBinary::MYSQL_TYPE_DECIMAL ProtocolBinary::MYSQL_TYPE_NEWDECIMAL

字段

value(lencenc_str)---string

示例

03 66 6f 6f -- string = "foo"

ProtocolBinary::MYSQL_TYPE_LONGLONG

字段

value (8) -- integer

示例

01 00 00 00 00 00 00 00 -- int64 = 1

ProtocolBinary::MYSQL_TYPE_LONG, ProtocolBinary::MYSQL_TYPE_INT24

字段

value (4) -- integer

示例

01 00 00 00 -- int32 = 1

ProtocolBinary::MYSQL_TYPE_SHORT, ProtocolBinary::MYSQL_TYPE_YEAR

字段

value (2) -- integer

示例

01 00 -- int16 = 1

ProtocolBinary::MYSQL_TYPE_TINY

字段

value (1) -- integer

示例

01 -- int8 = 1

ProtocolBinary::MYSQL_TYPE_DOUBLE

MYSQL_TYPE_DOUBLE以IEEE 754双精度格式存储浮点数。

在C语言存储中,第一个字节是有效位的最后一个字节,即小端存储。

字段

value (string.fix_len) -- (len=8) double

示例

66 66 66 66 66 66 24 40 -- double = 10.2

ProtocolBinary::MYSQL_TYPE_FLOAT

MYSQL_TYPE_FLOAT以IEEE 754单精度格式存储浮点数。

字段

value (string.fix_len) -- (len=4) float

示例

33 33 23 41 -- float = 10.2

ProtocolBinary::MYSQL_TYPE_DATE, ProtocolBinary::MYSQL_TYPE_DATETIME, ProtocolBinary::MYSQL_TYPE_TIMESTAMP

在二进制协议中存储DATE,DATETIME和TIMESTAMP字段。

为了节省空间,可以压缩数据包:

  • 如果年,月,日,小时,分钟,秒和微秒均为0,那么长度为0,且不发生为0字段
  • 如果小时,分钟,秒和微秒均为0,那么长度为4,且不发送为0字段
  • 如果微秒为0,那么长度为7,并且不发送微秒字段
  • 其他情况长度为11

字段

  • length (1) -- number of bytes following (valid values: 0, 4, 7, 11)
  • year (2) -- year
  • month (1) -- month
  • day (1) -- day
  • hour (1) -- hour
  • minute (1) -- minutes
  • second (1) -- seconds
  • micro_second (4) -- micro-seconds

示例

0b da 07 0a 11 13 1b 1e 01 00 00 00 -- datetime 2010-10-17 19:27:30.000 001
04 da 07 0a 11                      -- date = 2010-10-17
0b da 07 0a 11 13 1b 1e 01 00 00 00 -- timestamp

ProtocolBinary::MYSQL_TYPE_TIME

在二进制协议中存储TIME字段。

为了节省空间,可以压缩数据包:

  • 如果天,小时,分钟,秒和微秒均为0,那么长度为0,且不发生为0字段
  • 如果微秒为0,那么长度为8,并且不发送微秒字段
  • 其他情况长度为12

字段

  • length (1) -- number of bytes following (valid values: 0, 8, 12)
  • is_negative (1) -- (1 if minus, 0 for plus)
  • days (4) -- days
  • hours (1) -- hours
  • minutes (1) -- minutes
  • seconds (1) -- seconds
  • micro_seconds (4) -- micro-seconds

示例

0c 01 78 00 00 00 13 1b 1e 01 00 00 00 -- time  -120d 19:27:30.000 001
08 01 78 00 00 00 13 1b 1e             -- time  -120d 19:27:30
01                                     -- time     0d 00:00:00

ProtocolBinary::MYSQL_TYPE_NULL

仅存储在NULL位图中。

上次修改: 21 May 2020