当前位置:Gxlcms > 数据库问题 > MySQL 通讯协议

MySQL 通讯协议

时间:2021-07-01 10:21:17 帮助过:17人阅读

每次读取一行都会通过 find_command() 函数进行检测,如果满足对应的命令,且对应的函数变量非空,则直接执行,如 clear,此时不需要输入分号即可;如果没有找到,则必须要等待输入分号。

int read_and_execute(bool interactive)
{
    while (!aborted) {
        if (!interactive) {                               // 是否为交互模式
            ... ...   // 非交互模式,直接执行
        } else {                                          // 交互模式
            char *prompt = ...;                           // 首先会设置提示符
            line = readline(prompt);                      // 从命令行读取
            if ( ... && (com= find_command(line))) {      // 从commands[]中查找
                (*com->func)(&glob_buffer,line);          // 如果是help、edit等指令,则直接执行
            }
            add_line(...);                                // 常见的SQL,最终在此执行
        }
    }
}

int com_go(String *buffer,char *line)
{
    timer=start_timer();                                                // 设置时间
    error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length());   // 执行查询SQL
    do {
        // 获取结果
    } while(!(err= mysql_next_result(&mysql)));
}

在 add_line() 函数中,最终会调用 com_go() 函数,该函数是执行的主要函数,会最终调用 MySQL API 执行相应的 SQL、返回结果、输出时间等统计信息。

服务端

服务端通过 network_init() 执行一系列初始化之后,会阻塞在 handle_connections_sockets() 函数的 select()/poll() 函数处。

对于 one_thread_per_connection 这种方式,会新建一个线程执行 handle_one_connection() 。

handle_one_connection()
 |-thd_prepare_connection()
   |-login_connection()
     |-check_connection()
       |-acl_authenticate()

源码内容如下。

/* sql/sql_connect.cc */
int check_connection(THD *thd)
{
    if (!thd->main_security_ctx.host) {  // 通过TCP/IP连接,或者本地用-h 127.1
         if (acl_check_host(...))        // 检查hostname
    } else {                             // 使用unix sock连接,不会进行检测
        ... ...
    }
    return acl_authenticate(thd, connect_errors, 0)
}

/* sql/sql_acl.cc */
bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len)
{
    if (command == COM_CHANGE_USER) {

    } else {
        do_auth_once()                      // 执行认证模式

    }
}

在 acl_check_host() 会检查两个对象,一个是 hash 表 acl_check_hosts;另一个是动态数组 acl_wild_hosts 。这2个对象是在启动的时候,通过 init_check_host() 从 mysq.users 表里读出并加载的,其中 acl_wild_hosts 用于存放有统配符的主机,acl_check_hosts 存放具体的主机。

最终会调用 acl_authenticate() 这是主要的认证函数。

插件实现

MySQL 的认证授权采用插件实现。

默认采用 mysql_native_password 插件,也就是使用 native_password_auth_client() 作用户端的认证,实际有效的函数是 scramble()

上述的函数通过用户输入的 password、服务器返回的 scramble 生成 reply,返回给服务端;可以通过 password(‘string‘) 查看加密后的密文。

以 plugin/auth/ 目录下的插件为例,在启动服务器时,可添加 --plugin-load=auth_test_plugin.so参数自动加载相应的授权插件。

----- 获得foobar的加密格式
mysql> select password(‘foobar‘);
----- 旧的加密格式
mysql> select old_password(‘foobar‘);
----- 默认方式
mysql> create user ‘foobar2‘@‘localhost‘ identified via mysql_native_password using ‘xxx‘;

----- 也可以动态加载
mysql> install plugin test_plugin_server soname ‘auth_test_plugin.so‘;
----- 查看当前支持的插件
mysql> select * from information_schema.plugins where plugin_type=‘authentication‘;

mysql> create user ‘foobar‘@‘localhost‘ identified with test_plugin_server;
mysql> SET PASSWORD FOR ‘foobar‘@‘localhost‘=PASSWORD(‘new_password‘);
mysql> DROP USER ‘foobar‘@‘localhost‘;
mysql> FLUSH PRIVILEGES;
mysql> SELECT host, user, password, plugin FROM mysql.user;

在 plugin 目录下有很多 auth 插件可供参考,详细可参考官网 Writing Authentication Plugins 。

总结

在如下列举客户端与服务端的详细交互过程,其中客户端代码在 client 目录下。

### Client(mysql)  ###                       ### Server(mysqld) ###
----------------------------------------     --------------------------------------------------
main()                                       mysqld_main()
 |-sql_connect()                              |-init_ssl()
 | |-sql_real_connect() {for(;;)}             |-network_init()
 |   |-mysql_init()                           |-handle_connections_sockets()
 |   |-init_connection_options()                |-select()/poll()
 |   |-mysql_real_connect()                     |
 |     |-cli_mysql_real_connect()               |
 |       |-socket()                             |
 |       |-vio_new()                            |
 |       |-vio_socket_connect()                 |
 |       | |-mysql_socket_connect()             |
 |       |   |-connect()                        |
 |       |   |                                  |
 |       |   |        [Socket Connect]          |
 |       |   |>>==========>>==========>>======>>|
 |       |                                      |-accept()
 |       |-vio_keepalive()                      |-vio_new()
 |       |-my_net_set_read_timeout()            |-my_net_init()
 |       |-my_net_set_write_timeout()           |-create_new_thread()
 |       |-vio_io_wait()                          |-handle_one_connection()    {新线程}
 |       |                                          |-thd_prepare_connection() {for(;;)}
 |       |                                          | |-login_connection()
 |       |                                          |   |-check_connection()
 |       |                                          |     |-acl_check_host()
 |       |                                          |     |-vio_keepalive()
 |       |                                          |     |-acl_authenticate()
 |       |                                          |       |-do_auth_once()
 |       |                                          |       | |-native_password_authenticate()  {插件实现}
 |       |                                          |       |   |-create_random_string()
 |       |                                          |       |   |-send_server_handshake_packet()
 |       |                                          |       |   |
 |       |              [Handshake Initialization]  |       |   |
 |       |<<==<<==========<<==========<<==========<<==========<<|
 |       |-cli_safe_read()                          |       |   |-my_net_read()
 |       |-run_plugin_auth()                        |       |   |
 |       | |-native_password_auth_client()          |       |   |
 |       |   |-scramble()                           |       |   |
 |       |     |-my_net_write()                     |       |   |
 |       |     |                                    |       |   |
 |       |     |            [Client Authentication] |       |   |
 |       |     |>>==========>>==========>>==========>>========>>|
 |       |                                          |       |   |-check_scramble()
 |       |                                          |       |-mysql_change_db()
 |       |                                          |       |-my_ok()
 |       |                      [OK]                |       |
 |       |<<==========<<==========<<==========<<==========<<|
 |       |-cli_safe_read()                          |
 |                                                  |
 |                                                  |
 |                                                  |
 |                                                  |
 |-put_info() {Welcome Info}                        |
 |-read_and_execute() [for(;;)]                     |
                                                    |-thd_is_connection_alive()  [while()]
                                                    |-do_command()

参考

关于 MySQL的认证流程,包括客户端和服务器端,可以参考本地 MySQL认证协议;详细的协议介绍可以参考 MySQL Client/Server Protocol,或者 中文资料,或者保存的本地资料 MySQL服务器和客户端通信协议分析 。

MySQL 的认证授权可以采用插件,在 plugin 目录下有很多 auth 插件可供参考,具体可以参考官网的 MySQL Reference - Writing Authentication Plugins 。

MySQL 通讯协议

标签:标识   command   mys   ref   进制   变量   cut   从服务器   错误信息   

人气教程排行