MySQL源码分析之 通信协议(二)
MySQL源码分析之 通信协议(二)
mysql 通信包括,握手连接阶段、命令执行阶段和主从复制协议。在连接执行以下任务,客户端和服务器数据交换,如果请求加密设置 ssl 连接通道,根据服务器对客户端进行身份验证。
服务器和客户端完整交互如下:
1、登录认证交互报文
初始化握手,初始化握手从服务端发送 Protocol::Handshake 包开始,然后客户端可以选择使用 SSL 协议。接着客户端发送 Protocol::HandshakeResponse数据包。
接下来介绍一下 HandshakeV10数据报文,其他版本可以参考源码文档
protocol version: 服务协议版本号,恒为10,该值由 PROTOCOL_VERSION 宏定义决定(参考MySQL源代码/include/mysql_version.h
头文件定义)
server version: 服务版本信息,该值为字符串,由 MYSQL_SERVER_VERSION 宏定义决定(参考MySQL源代码/include/mysql_version.h
头文件定义)
thread id: 服务器为当前连接创建的线程 id
auth-plugin-data-part-1: 挑战随机数前8字节(第一部分),MySQL数据库用户认证采用的是挑战/应答的方式,服务器生成该挑战数并发送给客户端,由客户端进行处理并返回相应结果,然后服务器检查是否与预期的结果相同,从而完成用户认证的过程.
filler: 填充值 0x00, 终止挑战随机数前8字节。
capability_flags_1:服务权能标志的低16位
character_set: 字符编码,标识服务器用的字符集。只包含低8位。
status_flags: 服务器状态,状态值定义如下(参考MySQL源代码/include/mysql_com.h
中的宏定义)
enum SERVER_STATUS_flags_enum { SERVER_STATUS_IN_TRANS = 1, /**< 当使用显示事务活关闭自动提交时会触发*/ SERVER_STATUS_AUTOCOMMIT = 2, /**< 服务处于自动提交模式 */ SERVER_MORE_RESULTS_EXISTS = 8, /**< 使用 mulit query 时,存在下一条 query */ SERVER_QUERY_NO_GOOD_INDEX_USED = 16, /**< 查询没有使用好的索引 */ SERVER_QUERY_NO_INDEX_USED = 32, /**< 查询没有使用任何索引 */ /** 服务器能够满足客户机的请求,并为查询打开了一个只读的不可滚动游标,此标志用于响应COM_STMT_EXECUTE和COM_STMT_FETCH命令 由二进制协议结果集使用,表示必须使用COM_STMT_FETCH来获取行数据 @todo Refify Binary Protocol Resultset and COM_STMT_FETCH. */ SERVER_STATUS_CURSOR_EXISTS = 64, SERVER_STATUS_LAST_ROW_SENT = 128, /**< 当只读游标耗尽时,法师该标志用来响应COM_STMT_FETCH 命令*/ SERVER_STATUS_DB_DROPPED = 256, /**< 删除一个库 */ SERVER_STATUS_NO_BACKSLASH_ESCAPES = 512, SERVER_STATUS_METADATA_CHANGED = 1024, /**< 在一个 prepeaed 语句重复执行 发现新语句返回不同数量的结果集列,将此标记返回给客户端*/ SERVER_QUERY_WAS_SLOW = 2048, /**< 标记语句是否属于慢查询 */ SERVER_PS_OUT_PARAMS = 4096, /**< 标记含有输出参数的结果集 */ /**< 如果多语句事务是只读的,则与SERVER_STATUS_IN_TRANS同时设置。事务提交或者终止时清除 由于该标志以OK和EOF数据包的形式发送给客户端,因此该标志指示命令执行结束时的事务状态。*/ SERVER_STATUS_IN_TRANS_READONLY = 8192, SERVER_SESSION_STATE_CHANGED = (1UL << 14) /**< 如果启用此状态标志,则表示由于执行了最后一条语句,服务器上的一个状态信息已更改。 */ };
capability_flags_2: 服务权能标志的高16位。与客户端协商通讯方式,各标志位含义如下(参考MySQL源代码/include/mysql_com.h
中的宏定义)也可参考源码文档
auth_plugin_data_len: 如果auth_plugin_data_len > 0。组合身份验证插件数据的长度
00: 填充值,标志服务权能标志后八位结束
reserved:保留值都是0
auth-plugin-data-part-2:挑战随机数的第二部分
auth_plugin_name: 认证方法的名称
握手响应:
客户端接收到 handshake 包之后,生成一个响应并发给 server。如果Handshake Packet中的权能标识中带有client_protocol_41标识位,那么客户端将生成的Response为HandShake Response41,否则生成的为HandShake Response320,我们这里就不管HandShake Response320,因为现在的Server基本都会支持这个。
Type | Name | Description |
---|---|---|
int<4> | client_flag | Capabilities Flags, CLIENT_PROTOCOL_41 always set. |
int<4> | max_packet_size | maximum packet size 最大消息长度 |
int<1> | character_set | client charset a_protocol_character_set, only the lower 8-bits |
string[23] | filler | filler to the size of the handhshake response packet. All 0s. |
string<NUL> | username | login user name |
if capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA { | ||
string<length> | auth_response | opaque authentication response data generated by Authentication Method indicated by the plugin name field. |
} else { | ||
int<1> | auth_response_length | length of auth_response |
string<length> | auth_response | opaque authentication response data generated by Authentication Method indicated by the plugin name field. |
} | ||
if capabilities & CLIENT_CONNECT_WITH_DB { | ||
string<NUL> | database | initail database for the connection. This string should be interpreted using the character set indicated by character set field. |
} | ||
if capabilities & CLIENT_PLUGIN_AUTH { | ||
string<NUL> | client_plugin_name | the Authentication Method used by the client to generate auth-response value in this packet. This is an UTF-8 string. |
} | ||
if capabilities & CLIENT_CONNECT_ATTRS { | ||
int<lenenc> | length of all key-values | affected rows |
string<lenenc> | key1 | Name of the 1st client attribute |
string<lenenc> | value1 | Value of the 1st client attribute |
.. (if more data in length of all key-values, more keys and values parts) | ||
} | ||
int<1> | zstd_compression_level | compression level for zstd compression algorithm |
注:如果客户端和服务器版本不匹配,可能有其他情况发生,如果验证方法不匹配。服务器和客户端进行不同的处理。如AuthSwitchRequest请求,包括验证方法的介绍,请参考源码文档。
接下来为命令阶段:请参考http://hutaow.com/blog/2013/11/06/mysql-protocol-analysis/#1