星空网 > 软件开发 > 数据库

slave IO流程之一:mysql登陆过程(mysql_real_connect)

最近看了slave IO的源码,发现slave IO的写relay log貌似是单线程单连接的,这让我有点小失望。

slave IO的主函数是handle_slave_io,处理流程如下:

slave IO流程之一:mysql登陆过程(mysql_real_connect)

图1 handle_slave_io处理流程

 我们这次主要要完成safe_connect以及try_to_reconnet用到的核心函数 mysql_real_connect流程的探索。

一、mysql_real_connect流程

 在这之前我们需要弄明白连接mysql需要那几步操作,参考自官网的文档(http://dev.mysql.com/doc/internals/en/plain-handshake.html),据说连接时需要以下操作:

slave IO流程之一:mysql登陆过程(mysql_real_connect)

 图2 mysql_real_connect操作流程

1.建立与mysql的连接

slave IO流程之一:mysql登陆过程(mysql_real_connect)slave IO流程之一:mysql登陆过程(mysql_real_connect)
 1  if (!net->vio && 2    (!mysql->options.protocol || 3    mysql->options.protocol == MYSQL_PROTOCOL_SOCKET) && 4    (unix_socket || mysql_unix_port) && 5    (!host || !strcmp(host,LOCAL_HOST))) 6  { 7   my_socket sock= socket(AF_UNIX, SOCK_STREAM, 0); 8   DBUG_PRINT("info", ("Using socket")); 9   if (sock == SOCKET_ERROR)10   {11    set_mysql_extended_error(mysql, CR_SOCKET_CREATE_ERROR,12                 unknown_sqlstate,13                 ER(CR_SOCKET_CREATE_ERROR),14                 socket_errno);15    goto error;16   }17 18   net->vio= vio_new(sock, VIO_TYPE_SOCKET,19            VIO_LOCALHOST | VIO_BUFFERED_READ);20   if (!net->vio)21   {22    DBUG_PRINT("error",("Unknow protocol %d ", mysql->options.protocol));23    set_mysql_error(mysql, CR_CONN_UNKNOW_PROTOCOL, unknown_sqlstate);24    closesocket(sock);25    goto error;26   }27 28   host= LOCAL_HOST;29   if (!unix_socket)30    unix_socket= mysql_unix_port;31   host_info= (char*) ER(CR_LOCALHOST_CONNECTION);32   DBUG_PRINT("info", ("Using UNIX sock '%s'", unix_socket));33 34   memset(&UNIXaddr, 0, sizeof(UNIXaddr));35   UNIXaddr.sun_family= AF_UNIX;36   strmake(UNIXaddr.sun_path, unix_socket, sizeof(UNIXaddr.sun_path)-1);37 38   if (vio_socket_connect(net->vio, (struct sockaddr *) &UNIXaddr,39              sizeof(UNIXaddr), get_vio_connect_timeout(mysql)))40   {41    DBUG_PRINT("error",("Got error %d on connect to local server",42        socket_errno));43    set_mysql_extended_error(mysql, CR_CONNECTION_ERROR,44                 unknown_sqlstate,45                 ER(CR_CONNECTION_ERROR),46                 unix_socket, socket_errno);47    vio_delete(net->vio);48    net->vio= 0;49    goto error;50   }51   mysql->options.protocol=MYSQL_PROTOCOL_SOCKET;52  }

View Code
slave IO流程之一:mysql登陆过程(mysql_real_connect)slave IO流程之一:mysql登陆过程(mysql_real_connect)
 1 for (t_res= res_lst; t_res; t_res= t_res->ai_next) 2   { 3    DBUG_PRINT("info", ("Create socket, family: %d type: %d proto: %d", 4              t_res->ai_family, t_res->ai_socktype, 5              t_res->ai_protocol)); 6  7    sock= socket(t_res->ai_family, t_res->ai_socktype, t_res->ai_protocol); 8    if (sock == SOCKET_ERROR) 9    { 10     DBUG_PRINT("info", ("Socket created was invalid")); 11     /* Try next address if there is one */ 12     saved_error= socket_errno; 13     continue; 14    } 15  16    if (client_bind_ai_lst) 17    { 18     struct addrinfo* curr_bind_ai= NULL; 19     DBUG_PRINT("info", ("Attempting to bind socket to bind address(es)")); 20  21     /* 22       We'll attempt to bind to each of the addresses returned, until 23       we find one that works. 24       If none works, we'll try the next destination host address 25       (if any) 26     */ 27     curr_bind_ai= client_bind_ai_lst; 28  29     while (curr_bind_ai != NULL) 30     { 31      /* Attempt to bind the socket to the given address */ 32      bind_result= bind(sock, 33               curr_bind_ai->ai_addr, 34               curr_bind_ai->ai_addrlen); 35      if (!bind_result) 36       break;  /* Success */ 37  38      DBUG_PRINT("info", ("bind failed, attempting another bind address")); 39      /* Problem with the bind, move to next address if present */ 40      curr_bind_ai= curr_bind_ai->ai_next; 41     } 42  43     if (bind_result) 44     { 45      /* 46       Could not bind to any client-side address with this destination 47        Try the next destination address (if any) 48      */ 49      DBUG_PRINT("info", ("All bind attempts with this address failed")); 50      saved_error= socket_errno; 51      closesocket(sock); 52      continue; 53     } 54     DBUG_PRINT("info", ("Successfully bound client side of socket")); 55    } 56  57    /* Create a new Vio object to abstract the socket. */ 58    if (!net->vio) 59    { 60     if (!(net->vio= vio_new(sock, VIO_TYPE_TCPIP, flags))) 61     { 62      set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); 63      closesocket(sock); 64      freeaddrinfo(res_lst); 65      if (client_bind_ai_lst) 66       freeaddrinfo(client_bind_ai_lst); 67      goto error; 68     } 69    } 70    /* Just reinitialize if one is already allocated. */ 71    else if (vio_reset(net->vio, VIO_TYPE_TCPIP, sock, NULL, flags)) 72    { 73     set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate); 74     closesocket(sock); 75     freeaddrinfo(res_lst); 76     if (client_bind_ai_lst) 77      freeaddrinfo(client_bind_ai_lst); 78     goto error; 79    } 80  81    DBUG_PRINT("info", ("Connect socket")); 82    status= vio_socket_connect(net->vio, t_res->ai_addr, 83                 (socklen_t)t_res->ai_addrlen, 84                  get_vio_connect_timeout(mysql)); 85    /* 86     Here we rely on vio_socket_connect() to return success only if 87     the connect attempt was really successful. Otherwise we would 88     stop trying another address, believing we were successful. 89    */ 90    if (!status) 91     break; 92  93    /* 94     Save either the socket error status or the error code of 95     the failed vio_connection operation. It is necessary to 96     avoid having it overwritten by later operations. 97    */ 98    saved_error= socket_errno; 99 100    DBUG_PRINT("info", ("No success, try next address."));101   }

View Code

2.读取初始握手报文

slave IO流程之一:mysql登陆过程(mysql_real_connect)slave IO流程之一:mysql登陆过程(mysql_real_connect)
 1 if ((pkt_length=cli_safe_read(mysql, NULL)) == packet_error) 2  { 3   if (mysql->net.last_errno == CR_SERVER_LOST) 4    set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate, 5                 ER(CR_SERVER_LOST_EXTENDED), 6                "reading initial communication packet", 7                 socket_errno); 8   goto error; 9  }10  pkt_end= (char*)net->read_pos + pkt_length;11  /* Check if version of protocol matches current one */12  mysql->protocol_version= net->read_pos[0];13  DBUG_DUMP("packet",(uchar*) net->read_pos,10);14  DBUG_PRINT("info",("mysql protocol version %d, server=%d",15       PROTOCOL_VERSION, mysql->protocol_version));16  if (mysql->protocol_version != PROTOCOL_VERSION)17  {18   set_mysql_extended_error(mysql, CR_VERSION_ERROR, unknown_sqlstate,19               ER(CR_VERSION_ERROR), mysql->protocol_version,20                PROTOCOL_VERSION);21   goto error;22  }23  server_version_end= end= strend((char*) net->read_pos+1);24  mysql->thread_id=uint4korr((uchar*) end + 1);25  end+=5;26  /* 27   Scramble is split into two parts because old clients do not understand28   long scrambles; here goes the first part.29  */30  scramble_data= end;31  scramble_data_len= AUTH_PLUGIN_DATA_PART_1_LENGTH + 1;32  scramble_plugin= NULL;33  end+= scramble_data_len;34 35  if (pkt_end >= end + 1)36   mysql->server_capabilities=uint2korr((uchar*) end);37  if (pkt_end >= end + 18)38  {39   /* New protocol with 16 bytes to describe server characteristics */40   mysql->server_language=end[2];41   mysql->server_status=uint2korr((uchar*) end + 3);42   mysql->server_capabilities|= uint2korr((uchar*) end + 5) << 16;43   pkt_scramble_len= end[7];44   if (pkt_scramble_len < 0)45   {46    set_mysql_error(mysql, CR_MALFORMED_PACKET,47            unknown_sqlstate);    /* purecov: inspected */48    goto error;49   }50  }51  end+= 18;52 53  if (mysql_init_character_set(mysql))54   goto error;

View Code

3.发送回复握手报文

通过run_plugin_auth发送回复握手报文

slave IO流程之一:mysql登陆过程(mysql_real_connect)slave IO流程之一:mysql登陆过程(mysql_real_connect)
 1  mpvio.mysql_change_user= data_plugin == 0; 2  mpvio.cached_server_reply.pkt= (uchar*)data; 3  mpvio.cached_server_reply.pkt_len= data_len; 4  mpvio.read_packet= client_mpvio_read_packet; 5  mpvio.write_packet= client_mpvio_write_packet; 6  mpvio.info= client_mpvio_info; 7  mpvio.mysql= mysql; 8  mpvio.packets_read= mpvio.packets_written= 0; 9  mpvio.db= db;10  mpvio.plugin= auth_plugin;11 12  MYSQL_TRACE(AUTH_PLUGIN, mysql, (auth_plugin->name));13 14  res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);

View Code

slave IO流程之一:mysql登陆过程(mysql_real_connect)

 

slave IO流程之一:mysql登陆过程(mysql_real_connect)slave IO流程之一:mysql登陆过程(mysql_real_connect)
 1 static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) 2 { 3  int pkt_len; 4  uchar *pkt; 5  6  DBUG_ENTER("native_password_auth_client"); 7  8  9  if (((MCPVIO_EXT *)vio)->mysql_change_user)10  {11   /*12    in mysql_change_user() the client sends the first packet.13    we use the old scramble.14   */15   pkt= (uchar*)mysql->scramble;16   pkt_len= SCRAMBLE_LENGTH + 1;17  }18  else19  {20   /* read the scramble */21   if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)22    DBUG_RETURN(CR_ERROR);23 24   if (pkt_len != SCRAMBLE_LENGTH + 1)25    DBUG_RETURN(CR_SERVER_HANDSHAKE_ERR);26 27   /* save it in MYSQL */28   memcpy(mysql->scramble, pkt, SCRAMBLE_LENGTH);29   mysql->scramble[SCRAMBLE_LENGTH] = 0;30  }31 32  if (mysql->passwd[0])33  {34   char scrambled[SCRAMBLE_LENGTH + 1];35   DBUG_PRINT("info", ("sending scramble"));36   scramble(scrambled, (char*)pkt, mysql->passwd);37   if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH))38    DBUG_RETURN(CR_ERROR);39  }40  else41  {42   DBUG_PRINT("info", ("no password"));43   if (vio->write_packet(vio, 0, 0)) /* no password */44    DBUG_RETURN(CR_ERROR);45  }46 47  DBUG_RETURN(CR_OK);48 }

View Code

先通过read_packet获得挑战码,再通过scramble加密,然后通过write_packet发送回复握手报文。

client_mpvio_write_packet->send_client_reply_packet,该函数是发送回复握手报文。

4.读入认证回复报文

slave IO流程之一:mysql登陆过程(mysql_real_connect)slave IO流程之一:mysql登陆过程(mysql_real_connect)
1 /* read the OK packet (or use the cached value in mysql->net.read_pos */2  if (res == CR_OK)3   pkt_length= (*mysql->methods->read_change_user_result)(mysql);4  else /* res == CR_OK_HANDSHAKE_COMPLETE */5   pkt_length= mpvio.last_read_packet_len;

View Code

最后通过cli_read_change_user_result即cli_safe_read读取ok报文

5.选择数据库

slave IO流程之一:mysql登陆过程(mysql_real_connect)slave IO流程之一:mysql登陆过程(mysql_real_connect)
 1 int STDCALL 2 mysql_select_db(MYSQL *mysql, const char *db) 3 { 4  int error; 5  DBUG_ENTER("mysql_select_db"); 6  DBUG_PRINT("enter",("db: '%s'",db)); 7  8  if ((error=simple_command(mysql,COM_INIT_DB, (const uchar*) db, 9               (ulong) strlen(db),0)))10   DBUG_RETURN(error);11  my_free(mysql->db);12  mysql->db=my_strdup(key_memory_MYSQL,13            db,MYF(MY_WME));14  DBUG_RETURN(0);15 }

View Code

以command报文的形式发送命令数据

二、 登陆阶段所用到的报文格式

1.初始握手报文

slave IO流程之一:mysql登陆过程(mysql_real_connect)slave IO流程之一:mysql登陆过程(mysql_real_connect)
 1 1       [0a] protocol version 2 string[NUL]  server version 3 4       connection id 4 string[8]   auth-plugin-data-part-1 5 1       [00] filler 6 2       capability flags (lower 2 bytes) 7  if more data in the packet: 8 1       character set 9 2       status flags10 2       capability flags (upper 2 bytes)11  if capabilities & CLIENT_PLUGIN_AUTH {12 1       length of auth-plugin-data13  } else {14 1       [00]15  }16 string[10]   reserved (all [00])17  if capabilities & CLIENT_SECURE_CONNECTION {18 string[$len]  auth-plugin-data-part-2 ($len=MAX(13, length of auth-plugin-data - 8))19  if capabilities & CLIENT_PLUGIN_AUTH {20 string[NUL]  auth-plugin name21  }

View Code

2.回复握手报文

slave IO流程之一:mysql登陆过程(mysql_real_connect)slave IO流程之一:mysql登陆过程(mysql_real_connect)
 1 4       capability flags, CLIENT_PROTOCOL_41 always set 2 4       max-packet size 3 1       character set 4 string[23]   reserved (all [0]) 5 string[NUL]  username 6  if capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA { 7 lenenc-int   length of auth-response 8 string[n]   auth-response 9  } else if capabilities & CLIENT_SECURE_CONNECTION {10 1       length of auth-response11 string[n]   auth-response12  } else {13 string[NUL]  auth-response14  }15  if capabilities & CLIENT_CONNECT_WITH_DB {16 string[NUL]  database17  }18  if capabilities & CLIENT_PLUGIN_AUTH {19 string[NUL]  auth plugin name20  }21  if capabilities & CLIENT_CONNECT_ATTRS {22 lenenc-int   length of all key-values23 lenenc-str   key24 lenenc-str   value25  if-more data in 'length of all key-values', more keys and value pairs26  }

View Code

3.认证回复报文

slave IO流程之一:mysql登陆过程(mysql_real_connect)slave IO流程之一:mysql登陆过程(mysql_real_connect)
 1 Type  Name  Description 2 int<1>  header  [00] or [fe] the OK packet header 3 int<lenenc>  affected_rows  affected rows 4 int<lenenc>  last_insert_id  last insert-id 5 if capabilities & CLIENT_PROTOCOL_41 { 6  int<2>  status_flags  Status Flags 7  int<2>  warnings  number of warnings 8 } elseif capabilities & CLIENT_TRANSACTIONS { 9  int<2>  status_flags  Status Flags10 }11 if capabilities & CLIENT_SESSION_TRACK {12  string<lenenc>  info  human readable status information13  if status_flags & SERVER_SESSION_STATE_CHANGED {14   string<lenenc>  session_state_changes  session state info15  }16 } else {17  string<EOF>  info  human readable status information18 }

View Code

 




原标题:slave IO流程之一:mysql登陆过程(mysql_real_connect)

关键词:MYSQL

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

史上最全亚马逊卖家账号注册详解:https://www.ikjzd.com/articles/136731
亚马逊可以给外贸带来什么好处?:https://www.ikjzd.com/articles/136732
跨境平台那么多,凭什么要选亚马逊?:https://www.ikjzd.com/articles/136733
双十一后外贸人如何布局?:https://www.ikjzd.com/articles/136734
1-10月四川跨境电商实现交易额348.6亿元:https://www.ikjzd.com/articles/136735
亚马逊美国海外仓兴起的主要原因有哪些:https://www.ikjzd.com/articles/136736
雅鹿与时代赛跑52年,奔向品牌生态化丨亿邦智库 :https://www.kjdsnews.com/a/1836560.html
淘宝给商家发了个“大红包” :https://www.kjdsnews.com/a/1836561.html
相关文章
我的浏览记录
最新相关资讯
海外公司注册 | 跨境电商服务平台 | 深圳旅行社 | 东南亚物流