准备:
(一)服务器响应报文 🔗
当MySQL客户端成功连接MySQL服务端时,就可以可服务端进行sql交互了,比如use切换数据库、show展示所有数据库、CURD操作等,大体上分为,MySQL发送sql语句给MySQL服务端(上文中的客户端端请求报文)。MySQL服务端接收到sql语句后进行逻辑处理。处理完成以后MySQL服务端会返回相应的执行结果给客户端,MySQL客户端在收到响应报文后,需要首先检查第1个字节的值,来区分响应报文的类型。响应的数据包分为:OK_Packet
、Error_Packet
、EOF_Packet
、Result Set
、Data Field
、Field_Packet
:
响应报文类型 | 第1个字节取值范围 |
---|---|
OK 响应报文 | 0x00 |
Error 响应报文 | 0xFF |
Result Set 报文 | 0x01 - 0xFA |
Field 报文 | 0x01 - 0xFA |
Row Data 报文 | 0x01 - 0xFA |
EOF 报文 | 0xFE |
注: 响应报文的第1个字节在不同类型中含义不同,比如在OK报文中,该字节并没有实际意义,值恒为0x00;而在Result Set报文中,该字节又是长度编码的二进制数据结构(Length Coded Binary)中的第1字节。
(二)OK Packet 🔗
OK Packet数据格式 🔗
普通的OK包会在以下几种情况下产生,由MySQL服务端发送给相应的接收方:
- COM_PING: 连接或者测试数据库
- COM_QUERY: 不需要查询结果集的操作,比如INSERT, UPDATE, ALTER TABLE,DELETE
- COM_REFRESH: 数据刷新
- COM_REGISTER_SLAVE: 注册从服务器
首先前面的四个字节是包头,其中前三位代表packet数据长度,第四位是包序列号。以下是OK Packet的四字节后的内容:
MySQL 4.1 及之后的版本:
相对包内容的位置 | 长度(字节) | 名称 | 描述 |
---|---|---|---|
0 | 1 | 包头标识 | 0x00 代表这是一个OK 包 |
1 | rows_len | 影响行数 | 相应操作影响的行数,比如一个Update操作的记录是5条,那么这个值就为5 |
1 + rows_len | id_len | 自增id | 插入一条记录时,如果是自增id的话,返回的id值 |
1 + rows_len + id_len | 2 | 服务器状态 | 用于表示服务器状态,比如是否是事务模式或者自动提交模式 |
3 + rows_len + id_len | 2 | 警告数 | 上次命令引起的警告数 |
5 + rows_len + id_len | msg_len | 额外信息 | 此次操作的一些额外信息 |
Go代码实现:
type Success struct {
Row uint64
LastInsertId uint64
ServerStatus uint16
Warnings uint16
Message string
}
func (p *Packet) success(packet []byte) *Success {
sucPacket := &Success{}
row := make([]byte, 8)
copy(row, packet[0:1])
sucPacket.Row = binary.LittleEndian.Uint64(row)
LastInsertId := make([]byte, 8)
copy(LastInsertId, packet[1:2])
sucPacket.LastInsertId = binary.LittleEndian.Uint64(LastInsertId)
sucPacket.ServerStatus = binary.LittleEndian.Uint16(packet[2:4])
sucPacket.Warnings = binary.LittleEndian.Uint16(packet[4:6])
sucPacket.Message = string(packet[6:])
return sucPacket
}
(三)Error Packet 🔗
Error Packet数据格式 🔗
顾名思义Error 包就是当出现错误的时候返回的信息,比如账户验证不通过,查询命令不合法,非空字段未指定值等相关操作,MySQL服务端都会向MySQL客户端发送Error 包。
首先前面的四个字节是包头,其中前三位代表packet数据长度,第四位是包序列号。以下是Error Packet的四字节后的内容:
相对包内容的位置 | 长度(字节) | 名称 | 描述 |
---|---|---|---|
0 | 1 | 包头标识 | 0xFF 代表这是一个Error 包 |
1 | 2 | 错误代码 | 该错误的相应错误代码 |
3 | 1 | 标识位 | SQL执行状态标识位,用’#’进行标识 |
4 | 5 | 执行状态 | SQL的具体执行状态 |
9 | msg_len | 错误信息 | 具体的错误信息 |
Go代码实现:
type Error struct {
ErrorCode uint16 //该错误的相应错误代码
IdentificationBit []byte //标识位 SQL执行状态标识位,用’#’进行标识
SqlState string //执行状态 SQL的具体执行状态
ErrorMessage string //错误信息 具体的错误信息
}
func (p *Packet) error(packet []byte) *Error {
errorPacket := &Error{}
errorPacket.ErrorCode = binary.LittleEndian.Uint16(packet[0:2])
errorPacket.IdentificationBit = packet[2:3]
errorPacket.SqlState = string(packet[3:8])
errorPacket.ErrorMessage = string(packet[8:])
return errorPacket
}