为什么需要了解通信协议? 如果你在打算编写数据库的代理服务器、数据库的中间件(当然,你的通信方式是基于协议的,而不是使用其他的现成的插件)、对MySQL数据包进行审核的安全程序或者由于某些原因,需要了解底层的通信协议。 通信协议概述 服务器会使用T
| 第一个字节值 | 后续字节数 | 长度值说明 |
|---|---|---|
| 0-250 | 0 | 第一个字节值即为数据的真实长度 |
| 251 | 0 | 空数据,数据的真实长度为零 |
| 252 | 2 | 后续额外2个字节标识了数据的真实长度 |
| 253 | 3 | 后续额外3个字节标识了数据的真实长度 |
| 254 | 8 | 后续额外8个字节标识了数据的真实长度 |
/include/mysql_version.h头文件定义)
服务版本信息:该值为字符串,由 MYSQL_SERVER_VERSION 宏定义决定(参考MySQL源代码/include/mysql_version.h头文件定义)
服务器线程ID:服务器为当前连接所创建的线程ID。
挑战随机数:MySQL数据库用户认证采用的是挑战/应答的方式,服务器生成该挑战数并发送给客户端,由客户端进行处理并返回相应结果,然后服务器检查是否与预期的结果相同,从而完成用户认证的过程。
服务器权能标志:用于与客户端协商通讯方式,各标志位含义如下(参考MySQL源代码/include/mysql_com.h中的宏定义):
| 标志位名称 | 标志位 | 说明 |
|---|---|---|
| CLIENT_LONG_PASSWORD | 0x0001 | new more secure passwords |
| CLIENT_FOUND_ROWS | 0x0002 | Found instead of affected rows |
| CLIENT_LONG_FLAG | 0x0004 | Get all column flags |
| CLIENT_CONNECT_WITH_DB | 0x0008 | One can specify db on connect |
| CLIENT_NO_SCHEMA | 0x0010 | Do not allow database.table.column |
| CLIENT_COMPRESS | 0x0020 | Can use compression protocol |
| CLIENT_ODBC | 0x0040 | Odbc client |
| CLIENT_LOCAL_FILES | 0x0080 | Can use LOAD DATA LOCAL |
| CLIENT_IGNORE_SPACE | 0x0100 | Ignore spaces before ‘(’ |
| CLIENT_PROTOCOL_41 | 0x0200 | New 4.1 protocol |
| CLIENT_INTERACTIVE | 0x0400 | This is an interactive client |
| CLIENT_SSL | 0x0800 | Switch to SSL after handshake |
| CLIENT_IGNORE_SIGPIPE | 0x1000 | IGNORE sigpipes |
| CLIENT_TRANSACTIONS | 0x2000 | Client knows about transactions |
| CLIENT_RESERVED | 0x4000 | Old flag for 4.1 protocol |
| CLIENT_SECURE_CONNECTION | 0x8000 | New 4.1 authentication |
| CLIENT_MULTI_STATEMENTS | 0x0001 0000 | Enable/disable multi-stmt support |
| CLIENT_MULTI_RESULTS | 0x0002 0000 | Enable/disable multi-results |
/include/mysql_com.h中的宏定义):
| 状态名称 | 状态值 |
|---|---|
| SERVER_STATUS_IN_TRANS | 0x0001 |
| SERVER_STATUS_AUTOCOMMIT | 0x0002 |
| SERVER_STATUS_CURSOR_EXISTS | 0x0040 |
| SERVER_STATUS_LAST_ROW_SENT | 0x0080 |
| SERVER_STATUS_DB_DROPPED | 0x0100 |
| SERVER_STATUS_NO_BACKSLASH_ESCAPES | 0x0200 |
| SERVER_STATUS_METADATA_CHANGED | 0x0400 |
/include/mysql_com.h头文件中的定义):
| 类型值 | 命令 | 功能 | 关联函数 |
|---|---|---|---|
| 0x00 | COM_SLEEP | (内部线程状态) | (无) |
| 0x01 | COM_QUIT | 关闭连接 | mysql_close |
| 0x02 | COM_INIT_DB | 切换数据库 | mysql_select_db |
| 0x03 | COM_QUERY | SQL查询请求 | mysql_real_query |
| 0x04 | COM_FIELD_LIST | 获取数据表字段信息 | mysql_list_fields |
| 0x05 | COM_CREATE_DB | 创建数据库 | mysql_create_db |
| 0x06 | COM_DROP_DB | 删除数据库 | mysql_drop_db |
| 0x07 | COM_REFRESH | 清除缓存 | mysql_refresh |
| 0x08 | COM_SHUTDOWN | 停止服务器 | mysql_shutdown |
| 0x09 | COM_STATISTICS | 获取服务器统计信息 | mysql_stat |
| 0x0A | COM_PROCESS_INFO | 获取当前连接的列表 | mysql_list_processes |
| 0x0B | COM_CONNECT | (内部线程状态) | (无) |
| 0x0C | COM_PROCESS_KILL | 中断某个连接 | mysql_kill |
| 0x0D | COM_DEBUG | 保存服务器调试信息 | mysql_dump_debug_info |
| 0x0E | COM_PING | 测试连通性 | mysql_ping |
| 0x0F | COM_TIME | (内部线程状态) | (无) |
| 0x10 | COM_DELAYED_INSERT | (内部线程状态) | (无) |
| 0x11 | COM_CHANGE_USER | 重新登陆(不断连接) | mysql_change_user |
| 0x12 | COM_BINLOG_DUMP | 获取二进制日志信息 | (无) |
| 0x13 | COM_TABLE_DUMP | 获取数据表结构信息 | (无) |
| 0x14 | COM_CONNECT_OUT | (内部线程状态) | (无) |
| 0x15 | COM_REGISTER_SLAVE | 从服务器向主服务器进行注册 | (无) |
| 0x16 | COM_STMT_PREPARE | 预处理SQL语句 | mysql_stmt_prepare |
| 0x17 | COM_STMT_EXECUTE | 执行预处理语句 | mysql_stmt_execute |
| 0x18 | COM_STMT_SEND_LONG_DATA | 发送BLOB类型的数据 | mysql_stmt_send_long_data |
| 0x19 | COM_STMT_CLOSE | 销毁预处理语句 | mysql_stmt_close |
| 0x1A | COM_STMT_RESET | 清除预处理语句参数缓存 | mysql_stmt_reset |
| 0x1B | COM_SET_OPTION | 设置语句选项 | mysql_set_server_option |
| 0x1C | COM_STMT_FETCH | 获取预处理语句的执行结果 | mysql_stmt_fetch |
use hutaow;命令时(切换到hutaow数据库),发送的请求报文数据会是下面的样子:
0x02 0x68 0x75 0x74 0x61 0x6f 0x77其中,
0x02为请求类型值COM_INIT_DB,后面的0x68 0x75 0x74 0x61 0x6f 0x77为ASCII字符hutaow。
USE 。
| 字节 | 说明 |
|---|---|
| n | 数据库名称(字符串到达消息尾部时结束,无结束符) |
| 字节 | 说明 |
|---|---|
| n | SQL语句(字符串到达消息尾部时结束,无结束符) |
SHOW [FULL] FIELDS FROM ...。
| 字节 | 说明 |
|---|---|
| n | 表格名称(Null-Terminated String) |
| n | 字段(列)名称或通配符(可选) |
CREATE DATABASE代替。
| 字节 | 说明 |
|---|---|
| n | 数据库名称(字符串到达消息尾部时结束,无结束符) |
DROP DATABASE代替。
| 字节 | 说明 |
|---|---|
| n | 数据库名称(字符串到达消息尾部时结束,无结束符) |
FLUSH,或是执行mysqladmin flush-foo命令时发送该消息。
| 字节 | 说明 |
|---|---|
| 1 | 清除缓存选项(位图方式存储,各标志位含义如下) |
| 0x01: REFRESH_GRANT | |
| 0x02: REFRESH_LOG | |
| 0x04: REFRESH_TABLES | |
| 0x08: REFRESH_HOSTS | |
| 0x10: REFRESH_STATUS | |
| 0x20: REFRESH_THREADS | |
| 0x40: REFRESH_SLAVE | |
| 0x80: REFRESH_MASTER |
mysqladmin shutdown命令时发送该消息。
| 字节 | 说明 |
|---|---|
| 1 | 停止服务选项 |
| 0x00: SHUTDOWN_DEFAULT | |
| 0x01: SHUTDOWN_WAIT_CONNECTIONS | |
| 0x02: SHUTDOWN_WAIT_TRANSACTIONS | |
| 0x08: SHUTDOWN_WAIT_UPDATES | |
| 0x10: SHUTDOWN_WAIT_ALL_BUFFERS | |
| 0x11: SHUTDOWN_WAIT_CRITICAL_BUFFERS | |
| 0xFE: KILL_QUERY | |
| 0xFF: KILL_CONNECTION |
mysqladmin status命令时发送该消息,无参数。
SHOW PROCESSLIST,或是执行mysqladmin processlist命令时发送该消息,无参数。
KILL 。
| 字节 | 说明 |
|---|---|
| 4 | 连接ID号(小字节序) |
mysqladmin debug命令时发送该消息,无参数。
mysqladmin ping命令时发送该消息,无参数。
| 字节 | 说明 |
|---|---|
| n | 用户名(字符串以NULL结尾) |
| n | 密码(挑战数) |
| MySQL 3.23 版本:Null-Terminated String(长度9字节) | |
| MySQL 4.1 版本:Length Coded String(长度1+21字节) | |
| n | 数据库名称(Null-Terminated String) |
| 2 | 字符编码 |
| 字节 | 说明 |
|---|---|
| 4 | 二进制日志数据的起始位置(小字节序) |
| 4 | 二进制日志数据标志位(目前未使用,永远为0x00) |
| 4 | 从服务器的服务器ID值(小字节序) |
| n | 二进制日志的文件名称(可选,默认值为主服务器上第一个有效的文件名) |
LOAD TABLE ... FROM MASTER时发送该消息。目前该消息已过时,不再使用。
| 字节 | 说明 |
|---|---|
| n | 数据库名称(Length Coded String) |
| n | 数据表名称(Length Coded String) |
report_host变量设置的情况下,当备份连接时向主服务器发送的注册消息。
| 字节 | 说明 |
|---|---|
| 4 | 从服务器ID值(小字节序) |
| n | 主服务器IP地址(Length Coded String) |
| n | 主服务器用户名(Length Coded String) |
| n | 主服务器密码(Length Coded String) |
| 2 | 主服务器端口号 |
| 4 | 安全备份级别(由MySQL服务器rpl_recovery_rank变量设置,暂时未使用) |
| 4 | 主服务器ID值(值恒为0x00) |
| 字节 | 说明 |
|---|---|
| n | 带有“?”占位符的SQL语句(字符串到达消息尾部时结束,无结束符) |
| 字节 | 说明 |
|---|---|
| 4 | 预处理语句的ID值 |
| 1 | 标志位 |
| 0x00: CURSOR_TYPE_NO_CURSOR | |
| 0x01: CURSOR_TYPE_READ_ONLY | |
| 0x02: CURSOR_TYPE_FOR_UPDATE | |
| 0x04: CURSOR_TYPE_SCROLLABLE | |
| 4 | 保留(值恒为0x01) |
| 如果参数数量大于0 | |
| n | 空位图(Null-Bitmap,长度 = (参数数量 + 7) / 8 字节) |
| 1 | 参数分隔标志 |
| 如果参数分隔标志值为1 | |
| n | 每个参数的类型值(长度 = 参数数量 * 2 字节) |
| n | 每个参数的值 |
mysql_stmt_send_long_data函数)。
| 字节 | 说明 |
|---|---|
| 4 | 预处理语句的ID值(小字节序) |
| 2 | 参数序号(小字节序) |
| n | 数据负载(数据到达消息尾部时结束,无结束符) |
mysql_send_long_data函数)
| 字节 | 说明 |
|---|---|
| 4 | 预处理语句的ID值(小字节序) |
| 2 | 参数序号(小字节序) |
| 2 | 数据类型(未使用) |
| n | 数据负载(数据到达消息尾部时结束,无结束符) |
| 字节 | 说明 |
|---|---|
| 4 | 预处理语句的ID值(小字节序) |
COM_LONG_DATA一起使用。
| 字节 | 说明 |
|---|---|
| 4 | 预处理语句的ID值(小字节序) |
/include/mysql_com.h头文件中定义的enum_mysql_set_option枚举类型:
| 字节 | 说明 |
|---|---|
| 2 | 选项值(小字节序) |
| 字节 | 说明 |
|---|---|
| 4 | 预处理语句的ID值(小字节序) |
| 4 | 数据的行数(小字节序) |
| 响应报文类型 | 第1个字节取值范围 |
|---|---|
| OK 响应报文 | 0x00 |
| Error 响应报文 | 0xFF |
| Result Set 报文 | 0x01 - 0xFA |
| Field 报文 | 0x01 - 0xFA |
| Row Data 报文 | 0x01 - 0xFA |
| EOF 报文 | 0xFE |
| 字节 | 说明 |
|---|---|
| 1 | OK报文,值恒为0x00 |
| 1-9 | 受影响行数(Length Coded Binary) |
| 1-9 | 索引ID值(Length Coded Binary) |
| 2 | 服务器状态 |
| n | 服务器消息(字符串到达消息尾部时结束,无结束符) |
| 字节 | 说明 |
|---|---|
| 1 | OK报文,值恒为0x00 |
| 1-9 | 受影响行数(Length Coded Binary) |
| 1-9 | 索引ID值(Length Coded Binary) |
| 2 | 服务器状态 |
| 2 | 告警计数 |
| n | 服务器消息(字符串到达消息尾部时结束,无结束符,可选) |
INSERT/UPDATE/DELETE语句时所影响的数据行数。
索引ID值:该值为AUTO_INCREMENT索引字段生成,如果没有索引字段,则为0x00。注意:当INSERT插入语句为多行数据时,该索引ID值为第一个插入的数据行索引值,而非最后一个。
服务器状态:客户端可以通过该值检查命令是否在事务处理中。
告警计数:告警发生的次数。
服务器消息:服务器返回给客户端的消息,一般为简单的描述性字符串,可选字段。
| 字节 | 说明 |
|---|---|
| 1 | Error报文,值恒为0xFF |
| 2 | 错误编号(小字节序) |
| n | 服务器消息 |
| 字节 | 说明 |
|---|---|
| 1 | Error报文,值恒为0xFF |
| 2 | 错误编号(小字节序) |
| 1 | 服务器状态标志,恒为‘#’字符 |
| 5 | 服务器状态(5个字符) |
| n | 服务器消息 |
/include/mysqld_error.h头文件中。
服务器状态:服务器将错误编号通过mysql_errno_to_sqlstate函数转换为状态值,状态值由5字节的ASCII字符组成,定义在源代码/include/sql_state.h头文件中。
服务器消息:错误消息字符串到达消息尾时结束,长度可以由消息头中的长度值计算得出。消息长度为0-512字节。
| 结构 | 说明 |
|---|---|
| [Result Set Header] | 列数量 |
| [Field] | 列信息(多个) |
| [EOF] | 列结束 |
| [Row Data] | 行数据(多个) |
| [EOF] | 数据结束 |
| 字节 | 说明 |
|---|---|
| 1-9 | Field结构计数(Length Coded Binary) |
| 1-9 | 额外信息(Length Coded Binary) |
SHOW COLUMNS这种语句的执行结果才会用到额外信息(标识表格的列数量)。
| 字节 | 说明 |
|---|---|
| n | 数据表名称(Length Coded String) |
| n | 列(字段)名称(Length Coded String) |
| 4 | 列(字段)长度(Length Coded String) |
| 2 | 列(字段)类型(Length Coded String) |
| 2 | 列(字段)标志(Length Coded String) |
| 1 | 整型值精度 |
| n | 默认值(Length Coded String) |
| 字节 | 说明 |
|---|---|
| n | 目录名称(Length Coded String) |
| n | 数据库名称(Length Coded String) |
| n | 数据表名称(Length Coded String) |
| n | 数据表原始名称(Length Coded String) |
| n | 列(字段)名称(Length Coded String) |
| 4 | 列(字段)原始名称(Length Coded String) |
| 1 | 填充值 |
| 2 | 字符编码 |
| 4 | 列(字段)长度 |
| 1 | 列(字段)类型 |
| 2 | 列(字段)标志 |
| 1 | 整型值精度 |
| 2 | 填充值(0x00) |
| n | 默认值(Length Coded String) |
AS之后的名称)。
数据表原始名称:数据表的原始名称(AS之前的名称)。
列(字段)名称:列(字段)的别名(AS之后的名称)。
列(字段)原始名称:列(字段)的原始名称(AS之前的名称)。
字符编码:列(字段)的字符编码值。
列(字段)长度:列(字段)的长度值,真实长度可能小于该值,例如VARCHAR(2)类型的字段实际只能存储1个字符。
列(字段)类型:列(字段)的类型值,取值范围如下(参考源代码/include/mysql_com.h头文件中的enum_field_type枚举类型定义):
| 类型值 | 名称 |
|---|---|
| 0x00 | FIELD_TYPE_DECIMAL |
| 0x01 | FIELD_TYPE_TINY |
| 0x02 | FIELD_TYPE_SHORT |
| 0x03 | FIELD_TYPE_LONG |
| 0x04 | FIELD_TYPE_FLOAT |
| 0x05 | FIELD_TYPE_DOUBLE |
| 0x06 | FIELD_TYPE_NULL |
| 0x07 | FIELD_TYPE_TIMESTAMP |
| 0x08 | FIELD_TYPE_LONGLONG |
| 0x09 | FIELD_TYPE_INT24 |
| 0x0A | FIELD_TYPE_DATE |
| 0x0B | FIELD_TYPE_TIME |
| 0x0C | FIELD_TYPE_DATETIME |
| 0x0D | FIELD_TYPE_YEAR |
| 0x0E | FIELD_TYPE_NEWDATE |
| 0x0F | FIELD_TYPE_VARCHAR (new in MySQL 5.0) |
| 0x10 | FIELD_TYPE_BIT (new in MySQL 5.0) |
| 0xF6 | FIELD_TYPE_NEWDECIMAL (new in MYSQL 5.0) |
| 0xF7 | FIELD_TYPE_ENUM |
| 0xF8 | FIELD_TYPE_SET |
| 0xF9 | FIELD_TYPE_TINY_BLOB |
| 0xFA | FIELD_TYPE_MEDIUM_BLOB |
| 0xFB | FIELD_TYPE_LONG_BLOB |
| 0xFC | FIELD_TYPE_BLOB |
| 0xFD | FIELD_TYPE_VAR_STRING |
| 0xFE | FIELD_TYPE_STRING |
| 0xFF | FIELD_TYPE_GEOMETRY |
/include/mysql_com.h头文件中的宏定义):
| 标志位 | 名称 |
|---|---|
| 0x0001 | NOT_NULL_FLAG |
| 0x0002 | PRI_KEY_FLAG |
| 0x0004 | UNIQUE_KEY_FLAG |
| 0x0008 | MULTIPLE_KEY_FLAG |
| 0x0010 | BLOB_FLAG |
| 0x0020 | UNSIGNED_FLAG |
| 0x0040 | ZEROFILL_FLAG |
| 0x0080 | BINARY_FLAG |
| 0x0100 | ENUM_FLAG |
| 0x0200 | AUTO_INCREMENT_FLAG |
| 0x0400 | TIMESTAMP_FLAG |
| 0x0800 | SET_FLAG |
DECIMAL和NUMERIC类型的数值字段有效,用于标识数值的精度(小数点位置)。
默认值:该字段用在数据表定义中,普通的查询结果中不会出现。
附:Field结构的相关处理函数:
/client/client.c源文件中的unpack_fields函数/sql/sql_base.cc源文件中的send_fields函数| 字节 | 说明 |
|---|---|
| 1 | EOF值(0xFE) |
| 字节 | 说明 |
|---|---|
| 1 | EOF值(0xFE) |
| 2 | 告警计数 |
| 2 | 状态标志位 |
SERVER_MORE_RESULTS_EXISTS这样的标志位。
注:由于EOF值与其它Result Set结构共用1字节,所以在收到报文后需要对EOF包的真实性进行校验,校验条件为:
protocol.cc源文件中的send_eof函数| 字节 | 说明 |
|---|---|
| n | 字段值(Length Coded String) |
| … | (一行数据中包含多个字段值) |
/client/client.c源文件中的read_rows函数| 字节 | 说明 |
|---|---|
| 1 | 结构头(0x00) |
| (列数量 + 7 + 2) / 8 | 空位图 |
| n | 字段值 |
| … | (一行数据中包含多个字段值) |
| 结构 | 说明 |
|---|---|
| [PREPARE_OK] | PREPARE_OK结构 |
| 如果参数数量大于0 | |
| [Field] | 与Result Set消息结构相同 |
| [EOF] | |
| 如果列数大于0 | |
| [Field] | 与Result Set消息结构相同 |
| [EOF] |
| 字节 | 说明 |
|---|---|
| 1 | OK报文,值为0x00 |
| 4 | 预处理语句ID值 |
| 2 | 列数量 |
| 2 | 参数数量 |
| 1 | 填充值(0x00) |
| 2 | 告警计数 |
| 字节 | 说明 |
|---|---|
| 2 | 类型 |
| 2 | 标志 |
| 1 | 数值精度 |
| 4 | 字段长度 |
原文地址:数据库–MySQL服务器和客户端通信协议分析, 感谢原作者分享。