mysql 协议的服务端握手包及对其解析

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: 概况mysql客户端登陆到mysql服务端需要一个交互的过程,这里先看服务端给客户端发送的初始握手包。

概况

mysql客户端登陆到mysql服务端需要一个交互的过程,这里先看服务端给客户端发送的初始握手包。如下,client通过socket连接到server指定的端口后,server将往client发送初始握手包。服务端会根据不同的服务版本和不同的配置返回不同的初始化握手包。

client              server
   |------connect---- >|
   |                   |
   |<----handshake-----|
   |                   |
   |                   |
   |                   |

mysql通信报文结构

类型 名字 描述
int<3> payload长度 按照the least significant byte first存储,3个字节的payload和1个字节的序列号组合成报文头
int<1> 序列号
string payload 报文体,长度即为前面指定的payload长度

初始握手包

HandshakeV10协议如下

1              [0a] protocol version
string[NUL]    server version
4              connection id
string[8]      auth-plugin-data-part-1
1              [00] filler
2              capability flags (lower 2 bytes)
  if more data in the packet:
1              character set
2              status flags
2              capability flags (upper 2 bytes)
  if capabilities & CLIENT_PLUGIN_AUTH {
1              length of auth-plugin-data
  } else {
1              [00]
  }
string[10]     reserved (all [00])
  if capabilities & CLIENT_SECURE_CONNECTION {
string[$len]   auth-plugin-data-part-2 ($len=MAX(13, length of auth-plugin-data - 8))
  if capabilities & CLIENT_PLUGIN_AUTH {
    if version >= (5.5.7 and < 5.5.10) or (>= 5.6.0 and < 5.6.2) {
string[EOF]    auth-plugin name
    } elseif version >= 5.5.10 or >= 5.6.2 {
string[NUL]    auth-plugin name
    }
  }

更多详情 : http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake

生成初始握手包

  1. 定义版
/**
 * 
 * @author seaboat
 * @date 2016-09-25
 * @version 1.0
 * <pre><b>email: </b>849586227@qq.com</pre>
 * <pre><b>blog: </b>http://blog.csdn.net/wangyangzhizhou</pre>
 * <p>proxy's version.</p>
 */
public interface Versions {

    byte PROTOCOL_VERSION = 10;

    byte[] SERVER_VERSION = "5.6.0-snapshot".getBytes();
}
  1. 随机数工具
/**
 * 
 * @author seaboat
 * @date 2016-09-25
 * @version 1.0
 * <pre><b>email: </b>849586227@qq.com</pre>
 * <pre><b>blog: </b>http://blog.csdn.net/wangyangzhizhou</pre>
 * <p>a random util .</p>
 */
public class RandomUtil {
    private static final byte[] bytes = { '1', '2', '3', '4', '5', '6', '7',
            '8', '9', '0', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p',
            'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v',
            'b', 'n', 'm', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P',
            'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'Z', 'X', 'C', 'V',
            'B', 'N', 'M' };
    private static final long multiplier = 0x5DEECE66DL;
    private static final long addend = 0xBL;
    private static final long mask = (1L << 48) - 1;
    private static final long integerMask = (1L << 33) - 1;
    private static final long seedUniquifier = 8682522807148012L;

    private static long seed;
    static {
        long s = seedUniquifier + System.nanoTime();
        s = (s ^ multiplier) & mask;
        seed = s;
    }

    public static final byte[] randomBytes(int size) {
        byte[] bb = bytes;
        byte[] ab = new byte[size];
        for (int i = 0; i < size; i++) {
            ab[i] = randomByte(bb);
        }
        return ab;
    }

    private static byte randomByte(byte[] b) {
        int ran = (int) ((next() & integerMask) >>> 16);
        return b[ran % b.length];
    }

    private static long next() {
        long oldSeed = seed;
        long nextSeed = 0L;
        do {
            nextSeed = (oldSeed * multiplier + addend) & mask;
        } while (oldSeed == nextSeed);
        seed = nextSeed;
        return nextSeed;
    }

}
  1. mysql包基类
/**
 * 
 * @author seaboat
 * @date 2016-09-25
 * @version 1.0
 * <pre><b>email: </b>849586227@qq.com</pre>
 * <pre><b>blog: </b>http://blog.csdn.net/wangyangzhizhou</pre>
 * <p>MySQLPacket is mysql's basic packet.</p>
 */
public abstract class MySQLPacket {
    /**
     * none, this is an internal thread state
     */
    public static final byte COM_SLEEP = 0;

    /**
     * mysql_close
     */
    public static final byte COM_QUIT = 1;

    /**
     * mysql_select_db
     */
    public static final byte COM_INIT_DB = 2;

    /**
     * mysql_real_query
     */
    public static final byte COM_QUERY = 3;

    /**
     * mysql_list_fields
     */
    public static final byte COM_FIELD_LIST = 4;

    /**
     * mysql_create_db (deprecated)
     */
    public static final byte COM_CREATE_DB = 5;

    /**
     * mysql_drop_db (deprecated)
     */
    public static final byte COM_DROP_DB = 6;

    /**
     * mysql_refresh
     */
    public static final byte COM_REFRESH = 7;

    /**
     * mysql_shutdown
     */
    public static final byte COM_SHUTDOWN = 8;

    /**
     * mysql_stat
     */
    public static final byte COM_STATISTICS = 9;

    /**
     * mysql_list_processes
     */
    public static final byte COM_PROCESS_INFO = 10;

    /**
     * none, this is an internal thread state
     */
    public static final byte COM_CONNECT = 11;

    /**
     * mysql_kill
     */
    public static final byte COM_PROCESS_KILL = 12;

    /**
     * mysql_dump_debug_info
     */
    public static final byte COM_DEBUG = 13;

    /**
     * mysql_ping
     */
    public static final byte COM_PING = 14;

    /**
     * none, this is an internal thread state
     */
    public static final byte COM_TIME = 15;

    /**
     * none, this is an internal thread state
     */
    public static final byte COM_DELAYED_INSERT = 16;

    /**
     * mysql_change_user
     */
    public static final byte COM_CHANGE_USER = 17;

    /**
     * used by slave server mysqlbinlog
     */
    public static final byte COM_BINLOG_DUMP = 18;

    /**
     * used by slave server to get master table
     */
    public static final byte COM_TABLE_DUMP = 19;

    /**
     * used by slave to log connection to master
     */
    public static final byte COM_CONNECT_OUT = 20;

    /**
     * used by slave to register to master
     */
    public static final byte COM_REGISTER_SLAVE = 21;

    /**
     * mysql_stmt_prepare
     */
    public static final byte COM_STMT_PREPARE = 22;

    /**
     * mysql_stmt_execute
     */
    public static final byte COM_STMT_EXECUTE = 23;

    /**
     * mysql_stmt_send_long_data
     */
    public static final byte COM_STMT_SEND_LONG_DATA = 24;

    /**
     * mysql_stmt_close
     */
    public static final byte COM_STMT_CLOSE = 25;

    /**
     * mysql_stmt_reset
     */
    public static final byte COM_STMT_RESET = 26;

    /**
     * mysql_set_server_option
     */
    public static final byte COM_SET_OPTION = 27;

    /**
     * mysql_stmt_fetch
     */
    public static final byte COM_STMT_FETCH = 28;
    /**
     * mysql packet length
     */
    public int packetLength;
    /**
     * mysql packet id
     */
    public byte packetId;

    /**
     * calculate mysql packet length
     */
    public abstract int calcPacketSize();

    /**
     * mysql packet info
     */
    protected abstract String getPacketInfo();

    @Override
    public String toString() {
        return new StringBuilder().append(getPacketInfo()).append("{length=")
                .append(packetLength).append(",id=").append(packetId)
                .append('}').toString();
    }

}
  1. 握手包类
/**
 * 
 * @author seaboat
 * @date 2016-09-25
 * @version 1.0
 * <pre><b>email: </b>849586227@qq.com</pre>
 * <pre><b>blog: </b>http://blog.csdn.net/wangyangzhizhou</pre>
 * <p>AuthPacket means mysql initial handshake packet .</p>
 */
public class HandshakePacket extends MySQLPacket {
    private static final byte[] FILLER_13 = new byte[] { 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0 };

    public byte protocolVersion;
    public byte[] serverVersion;
    public long threadId;
    public byte[] seed;
    public int serverCapabilities;
    public byte serverCharsetIndex;
    public int serverStatus;
    public byte[] restOfScrambleBuff;

    public void read(byte[] data) {
        MySQLMessage mm = new MySQLMessage(data);
        packetLength = mm.readUB3();
        packetId = mm.read();
        protocolVersion = mm.read();
        serverVersion = mm.readBytesWithNull();
        threadId = mm.readUB4();
        seed = mm.readBytesWithNull();
        serverCapabilities = mm.readUB2();
        serverCharsetIndex = mm.read();
        serverStatus = mm.readUB2();
        mm.move(13);
        restOfScrambleBuff = mm.readBytesWithNull();
    }

    @Override
    public int calcPacketSize() {
        int size = 1;
        size += serverVersion.length;// n
        size += 5;// 1+4
        size += seed.length;// 8
        size += 19;// 1+2+1+2+13
        size += restOfScrambleBuff.length;// 12
        size += 1;// 1
        return size;
    }

    public void write(ByteBuffer buffer) {
        BufferUtil.writeUB3(buffer, calcPacketSize());
        buffer.put(packetId);
        buffer.put(protocolVersion);
        BufferUtil.writeWithNull(buffer, serverVersion);
        BufferUtil.writeUB4(buffer, threadId);
        BufferUtil.writeWithNull(buffer, seed);
        BufferUtil.writeUB2(buffer, serverCapabilities);
        buffer.put(serverCharsetIndex);
        BufferUtil.writeUB2(buffer, serverStatus);
        buffer.put(FILLER_13);
        BufferUtil.writeWithNull(buffer, restOfScrambleBuff);
    }

    @Override
    protected String getPacketInfo() {
        return "MySQL Handshake Packet";
    }

}
  1. 服务端能力类
/**
 * 
 * @author seaboat
 * @date 2016-09-25
 * @version 1.0
 * <pre><b>email: </b>849586227@qq.com</pre>
 * <pre><b>blog: </b>http://blog.csdn.net/wangyangzhizhou</pre>
 * <p>server capabilities .</p>
 */
public interface Capabilities {

    // new more secure passwords
    int CLIENT_LONG_PASSWORD = 1;

    // Found instead of affected rows
    int CLIENT_FOUND_ROWS = 2;

    // Get all column flags
    int CLIENT_LONG_FLAG = 4;

    // One can specify db on connect
    int CLIENT_CONNECT_WITH_DB = 8;

    // Don't allow database.table.column
    int CLIENT_NO_SCHEMA = 16;

    // Can use compression protocol
    int CLIENT_COMPRESS = 32;

    // Odbc client
    int CLIENT_ODBC = 64;

    // Can use LOAD DATA LOCAL
    int CLIENT_LOCAL_FILES = 128;

    // Ignore spaces before '('
    int CLIENT_IGNORE_SPACE = 256;

    // New 4.1 protocol This is an interactive client
    int CLIENT_PROTOCOL_41 = 512;

    // This is an interactive client
    int CLIENT_INTERACTIVE = 1024;

    // Switch to SSL after handshake
    int CLIENT_SSL = 2048;

    // IGNORE sigpipes
    int CLIENT_IGNORE_SIGPIPE = 4096;

    // Client knows about transactions
    int CLIENT_TRANSACTIONS = 8192;

    // Old flag for 4.1 protocol
    int CLIENT_RESERVED = 16384;

    // New 4.1 authentication
    int CLIENT_SECURE_CONNECTION = 32768;

    // Enable/disable multi-stmt support
    int CLIENT_MULTI_STATEMENTS = 65536;

    // Enable/disable multi-results
    int CLIENT_MULTI_RESULTS = 131072;

}
  1. 测试类
/**
 * 
 * @author seaboat
 * @date 2016-09-25
 * @version 1.0
 * <pre><b>email: </b>849586227@qq.com</pre>
 * <pre><b>blog: </b>http://blog.csdn.net/wangyangzhizhou</pre>
 * <p>test handshake packet.</p>
 */
public class HandshakePacketTest {
    private final static byte[] hex = "0123456789ABCDEF".getBytes();

    @Test
    public void produce() {
        byte[] rand1 = RandomUtil.randomBytes(8);
        byte[] rand2 = RandomUtil.randomBytes(12);
        byte[] seed = new byte[rand1.length + rand2.length];
        System.arraycopy(rand1, 0, seed, 0, rand1.length);
        System.arraycopy(rand2, 0, seed, rand1.length, rand2.length);
        HandshakePacket hs = new HandshakePacket();
        hs.packetId = 0;
        hs.protocolVersion = Versions.PROTOCOL_VERSION;
        hs.serverVersion = Versions.SERVER_VERSION;
        hs.threadId = 1000;
        hs.seed = rand1;
        hs.serverCapabilities = getServerCapabilities();
        hs.serverCharsetIndex = (byte) (CharsetUtil.getIndex("utf8") & 0xff);
        hs.serverStatus = 2;
        hs.restOfScrambleBuff = rand2;

        ByteBuffer buffer = ByteBuffer.allocate(256);
        hs.write(buffer);
        buffer.flip();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes, 0, bytes.length);
        String result = Bytes2HexString(bytes);
        assertTrue(Integer.valueOf(result.substring(0,2),16)==result.length()/2-4);
    }

    public static String Bytes2HexString(byte[] b) {
        byte[] buff = new byte[2 * b.length];
        for (int i = 0; i < b.length; i++) {
            buff[2 * i] = hex[(b[i] >> 4) & 0x0f];
            buff[2 * i + 1] = hex[b[i] & 0x0f];
        }
        return new String(buff);
    }

    public static String str2HexStr(String str) {
        char[] chars = "0123456789ABCDEF".toCharArray();
        StringBuilder sb = new StringBuilder("");
        byte[] bs = str.getBytes();
        int bit;
        for (int i = 0; i < bs.length; i++) {
            bit = (bs[i] & 0x0f0) >> 4;
            sb.append(chars[bit]);
            bit = bs[i] & 0x0f;
            sb.append(chars[bit]);
        }
        return sb.toString();
    }

    protected int getServerCapabilities() {
        int flag = 0;
        flag |= Capabilities.CLIENT_LONG_PASSWORD;
        flag |= Capabilities.CLIENT_FOUND_ROWS;
        flag |= Capabilities.CLIENT_LONG_FLAG;
        flag |= Capabilities.CLIENT_CONNECT_WITH_DB;
        flag |= Capabilities.CLIENT_ODBC;
        flag |= Capabilities.CLIENT_IGNORE_SPACE;
        flag |= Capabilities.CLIENT_PROTOCOL_41;
        flag |= Capabilities.CLIENT_INTERACTIVE;
        flag |= Capabilities.CLIENT_IGNORE_SIGPIPE;
        flag |= Capabilities.CLIENT_TRANSACTIONS;
        flag |= Capabilities.CLIENT_SECURE_CONNECTION;
        return flag;
    }

}

========广告时间========

鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以到 https://item.jd.com/12185360.html 进行预定。感谢各位朋友。

为什么写《Tomcat内核设计剖析》

=========================

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
11天前
|
Ubuntu 关系型数据库 MySQL
MySQL二进制包安装
本文详细介绍了在多种Linux系统上通过二进制包安装MySQL 8.0和8.4版本的完整过程,涵盖用户创建、glibc版本匹配、程序解压、环境变量配置、初始化数据库及服务启动等步骤,并提供支持多发行版的一键安装脚本,助力高效部署MySQL环境。
109 4
MySQL二进制包安装
|
11天前
|
安全 关系型数据库 MySQL
MySQL包安装 -- SUSE系列(离线RPM包安装MySQL)
本文详细介绍在openSUSE系统上通过离线RPM包安装MySQL 8.0和8.4版本的完整步骤,包括下载地址、RPM包解压、GPG密钥导入、使用rpm或zypper命令安装及服务启动验证,涵盖初始密码获取与安全修改方法,适用于无网络环境下的MySQL部署。
203 3
MySQL包安装 -- SUSE系列(离线RPM包安装MySQL)
|
11天前
|
关系型数据库 MySQL Linux
MySQL包安装 -- SUSE系列(SUSE资源库安装MySQL)
本文介绍了在openSUSE系统上通过SUSE资源库安装MySQL 8.0和8.4版本的完整步骤,包括配置国内镜像源、安装MySQL服务、启动并验证运行状态,以及修改初始密码等操作,适用于希望在SUSE系列系统中快速部署MySQL的用户。
99 3
MySQL包安装 -- SUSE系列(SUSE资源库安装MySQL)
|
11天前
|
Ubuntu 关系型数据库 MySQL
MySQL包安装 -- Debian系列(离线DEB包安装MySQL)
本文详细介绍了在Ubuntu 24.04、22.04、20.04及Debian 12系统上,通过离线DEB包安装MySQL 8.0和8.4版本的完整步骤。涵盖下载地址、依赖处理、dpkg安装顺序、配置方法及服务启动验证,确保用户可顺利部署MySQL数据库。
181 0
MySQL包安装 -- Debian系列(离线DEB包安装MySQL)
|
11天前
|
运维 Ubuntu 关系型数据库
MySQL包安装 -- Debian系列(Apt资源库安装MySQL)
本文介绍了在Debian系列系统(如Ubuntu、Debian 11/12)中通过APT仓库安装MySQL 8.0和8.4版本的完整步骤,涵盖添加官方源、配置国内镜像、安装服务及初始化设置,并验证运行状态,适用于各类Linux运维场景。
163 0
MySQL包安装 -- Debian系列(Apt资源库安装MySQL)
|
11天前
|
Oracle 关系型数据库 MySQL
MySQL包安装 -- RHEL系列(离线RPM包安装MySQL)
本文详细介绍在Rocky、CentOS、AlmaLinux、openEuler等主流Linux系统上,通过离线RPM包安装MySQL 8.0和8.4版本的完整步骤,涵盖下载、依赖处理、rpm/yum安装、服务启动、密码设置等关键环节,适用于多种企业级环境部署需求。
284 0
MySQL包安装 -- RHEL系列(离线RPM包安装MySQL)
|
11天前
|
存储 关系型数据库 MySQL
MySQL介绍和MySQL包安装 -- RHEL系列(Yum资源库安装MySQL)
MySQL是一款开源关系型数据库,高性能、易用、跨平台,支持多种存储引擎,广泛应用于Web开发、企业级应用等领域。本教程介绍其特点、架构及在主流Linux系统中的安装配置方法。
278 0
MySQL介绍和MySQL包安装 -- RHEL系列(Yum资源库安装MySQL)
|
2月前
|
存储 SQL 关系型数据库
MySQL中binlog、redolog与undolog的不同之处解析
每个都扮演回答回溯与错误修正机构角色: BinLog像历史记载员详细记载每件大大小小事件; RedoLog则像紧急救援队伍遇见突發情況追踪最后活动轨迹尽力补救; UndoLog就类似时间机器可倒带历史让一切归位原始样貌同时兼具平行宇宙观察能让多人同时看见各自期望看见历程而互不干扰.
165 9
|
3月前
|
存储 SQL 关系型数据库
MySQL 核心知识与索引优化全解析
本文系统梳理了 MySQL 的核心知识与索引优化策略。在基础概念部分,阐述了 char 与 varchar 在存储方式和性能上的差异,以及事务的 ACID 特性、并发事务问题及对应的隔离级别(MySQL 默认 REPEATABLE READ)。 索引基础部分,详解了 InnoDB 默认的 B+tree 索引结构(多路平衡树、叶子节点存数据、双向链表支持区间查询),区分了聚簇索引(数据与索引共存,唯一)和二级索引(数据与索引分离,多个),解释了回表查询的概念及优化方法,并分析了 B+tree 作为索引结构的优势(树高低、效率稳、支持区间查询)。 索引优化部分,列出了索引创建的六大原则
|
3月前
|
存储 SQL 关系型数据库
MySQL 核心知识与性能优化全解析
我整理的这份内容涵盖了 MySQL 诸多核心知识。包括查询语句的书写与执行顺序,多表查询的连接方式及内、外连接的区别。还讲了 CHAR 和 VARCHAR 的差异,索引的类型、底层结构、聚簇与非聚簇之分,以及回表查询、覆盖索引、左前缀原则和索引失效情形,还有建索引的取舍。对比了 MyISAM 和 InnoDB 存储引擎的不同,提及性能优化的多方面方法,以及超大分页处理、慢查询定位与分析等,最后提到了锁和分库分表可参考相关资料。

推荐镜像

更多
下一篇
oss教程