用Python开发MySQL增强半同步BinlogServer(T2通信篇)

本文是python-mysql-binlogserver系列的第二篇,讲解如何用Python与MySQL进行交互,涉及MySQL通信协议基础、Greeting包解析、认证响应、查询命令的执行以及Binlog文件格式。通过实例展示了Python解析二进制流和处理MySQL通信的过程,为后续实战篇打下基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导读

作者:曾永伟,知数堂10期学员,多年JAVA物流行业开发管理经验和PHP/Python跨境电商开发管理经验,对数据库系统情有独钟,善于运用SQL编程简化业务逻辑,去年开始正式从业MySQL DBA, 专注于DB系统自动化运维、MySQL云上实践。

本文为python-mysql-binlogserver系列的第二篇(T2通信篇),之后会陆续发布此系列的其他文章,请大家点击在看或者收藏,自行练习。

一、概述

从前一篇文章我们已经了解了:

  • 二进制在计算机中的应用

  • 字符集与二进制的关系及其在Python中处理

  • 用struct来处理二进制的转换问题

  • Socket通信的基本编程方法

在本节中,我们将结合这些内容进一步来了解如何使Python和MySQL进行交互。

二、MySQL通信协议基础

在Python中,使用socket.secv接收数据或send发送数据的都是二进制流对象Bytes,我们需要结合MySQL通信协议来逐字节解析其具体的含义。

MySQL基础通信单位Packet,它由header + payload组成,header由3个字节的payload长度(最大16M字节数)和1个字节的流水号组成,在读取一个Packet时,通常先读4个字节,解析出payload的长度和payload的序号,再根据payload的长度把余下的正文读取出来。

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("127.0.0.1",  3306))
'''
# 先读Header
header = s.recv(4)
length = struct.unpack('<I', header[:3] + b'\x00')[0]
sequenceId = struct.unpack('<B', header[:-1])[0]  

# 再读余下的正文,完成一个Packet的完整读取
body = s.recv(length)

解析Greeting包

当Client连接上MySQL Server后,MySQL会主动发送一个greeting包,把自己的状态和随机密文发送给Client, 等待Client响应帐户和密码等信息,验证失败发送ERR包并主动断开连接,验证成功后发送OK包,保持连接,等待Client发送其它"指令"。

接下来我先看一下Greeting包正文的官方说明:

https://dev.mysql.com/doc/internals/en/connection-phase-packets.html
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)
1                character set
2                status flags
2                capability flags (upper 2 bytes)
1                length of auth-plugin-data
string[10]       reserved (all [00])
string[$len]     auth-plugin-data-part-2  ($len=MAX(13, length of auth-plugin-data -  8))
string[NUL]      auth-plugin name

为了更加直接的观察和理解,我这里去掉了对低版本的兼容格式,让其显得更加整洁。

我们来尝试用Python解析第一个Greeting包:

import socket
import  struct
from pprint import pprint

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.56.101",  3306))
 
greeting =  {}

# Header 3+1
greeting["length"]  =  struct.unpack('<I', s.recv(3)  + b'\x00')[0]
greeting["sequenceId"]  =  struct.unpack('<B', s.recv(1))[0]

# 正文开始
greeting["protocolVersion"]  = s.recv(1)

# serverVersion是string[NUL]类型,所以一直循环读取到\x00节束
greeting["serverVersion"]  =  ""
while  True:
      _byte = s.recv(1)
      if _byte == b'\x00':
          break
      greeting["serverVersion"]  += chr(int(_byte.hex(),  16))

# 余下部分请自行参照上方文档进行一一对照
greeting["connectionId"]  =  struct.unpack('<I', s.recv(4))[0]
greeting["challenge1"]  = s.recv(8).decode("utf8")
_filler = s.recv(1)
greeting["capabilityFlags"]  = s.recv(2)
greeting["characterSet"]  = s.recv(1)
greeting["statusFlags"]  = s.recv(2)
greeting["capabilityFlag"]  = s.recv(2)
greeting["authPluginDataLength"]  =  struct.unpack('<B', s.recv(1))[0]
_reserved = s.recv(10)
greeting["challenge2"]  = s.recv(max(13, greeting["authPluginDataLength"]  -  8)).decode("utf8")
greeting["authPluginName"]  =  ""
while  True:
     _byte = s.recv(1)
     if _byte == b'\x00':
         break
     greeting["authPluginName"]  += chr(int(_byte.hex(),  16))
pprin
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值