深度解析 PostgreSQL Protocol v3.0(一)

引言

PostgreSQL 使用基于消息的协议在前端(也可以称为客户端)和后端(也可以称为服务器)之间进行通信。该协议通过 TCP/IP 和 Unix 域套接字支持。

《深度解析 PostgreSQL Protocol v3.0》系列技术贴,将带大家深度了解 PostgreSQL Protocol 3.0 版本(在 PostgreSQL 7.4 及更高版本中实现,有关早期协议版本的描述请参考 PostgreSQL 文档的早期版本,该系列文章不予赘述)相关的消息传输格式和格式码消息支持的数据类型消息的格式协议交互流程错误消息和通知消息支持的子协议等,相关的代码解读基于 PostgreSQL 代码仓库的 REL_14_STABLE 分支。

PostgreSQL 单个服务器可以支持多个协议版本,可以接收和处理多个不同版本协议的客户端的请求消息。初始启动请求消息告诉服务器、客户端尝试使用的协议版本

  • 如果客户端请求的主要版本不受服务器支持,则连接将被拒绝(例如,如果客户端请求协议版本 4.0,而服务器端支持的协议版本不存在 4.0,此时就会发生这种情况);

  • 如果服务器不支持客户端请求的次要版本(例如,客户端请求版本为 3.1,但服务器仅支持 3.0,不支持 3.1 版本,此时就会发生这种情况),则服务器可以拒绝连接,或者可以使用包含其支持的最高次要协议版本的 NegotiateProtocolVersion 消息进行响应。

客户端可以选择使用服务器端指定的协议版本继续连接或中止连接。为了高效地为多个客户端提供服务,服务器为每个客户端启动一个新的进程进行请求处理。在当前实现中,在服务器检测到客户端的 Socket 连接后立即创建新的子进程进行后续的处理,比如 SSL 通信加密协商、启动消息、身份认证等流程。

一、消息传输的格式

客户端和服务器所有的交互都是通过消息流进行的。每一条消息主要由三部分组成:

  • 消息类型

用于标记消息的类型,是单个字符或者 1 位的数字。消息类型长度占用 1 个字节。

  • 消息长度

消息中除了消息类型之外的字节长度。消息长度占用 4 个字节。消息长度的值包含了消息长度本身的 4 个字节长度。计算方法:

(1)消息字节总长度减去 1 字节的消息类型的长度;

(2)消息内容字节总长度加上消息长度本身占用的字节数 4。

  • 消息体

消息的具体 payload 内容,例如简单查询的 SQL 内容。

需要注意的是,由于历史原因,客户端发送的第一条消息(启动消息)没有消息类型的 1 个字节。服务器和客户端为了避免与消息流失去同步,通常在尝试处理消息内容之前将整个消息读入缓冲区(使用字节计数)。

如果在处理消息内容时检测到错误,就可以轻松恢复。在极端情况下(例如没有足够的内存来缓冲消息),接收器可以使用字节计数来确定在恢复读取消息之前要跳过多少输入字节长度。服务器和客户端都必须注意不要发送不完整的消息。这通常是通过在开始发送之前在缓冲区中编码整个消息来完成的。

如果在发送或接收消息的过程中发生通信故障,那么唯一明智的做法是断开连接,因为恢复消息边界同步的希望很小。

二、消息支持的数据类型

PostgreSQL Protocol v3.0 的消息中支持的数据类型只有以下 4 种:

  • Intn(i)

位二进制表示的整数,为网络字节顺序(最高有效字节优先,MSB),表示该值占用的位数。

如果指定了 i则 是将出现的确切值;如果未指定 值,该值是可变的。例如,Int16 表示一个值未指定的占用 16 位二进制位的整数(占用长度为 2 个字节,占用 16 位二进制);Int32(42)表示一个值为 42 的占用 32 位二进制位的整数(占用长度为 4 个字节,占用 32 位二进制)。

  • Intn[k]

由 个 位二进制表示的整数组成的数组。数组长度 始终由消息中较早的字段确定。

  • String(s)

以空结尾的字符串(C-style 字符串)。字符串没有特定的长度限制。

如果指定了 s,则 是将出现的确切值;如果未指定 值,该值是可变的。例如,String 表示一个值未指定的字符串;String("user")表示值为 user 的字符串。

需要注意的是,服务器可以返回的字符串长度没有预定义长度的限制,因此客户端比较好的编码策略是使用可扩展缓冲区,以便可以接收适合内存大小的内容。如果这不可行,请读取整个字符串并丢弃不适合固定大小缓冲区的尾随字符。

  • Byten(c)

个字节。如果字段宽度 不是常数,则它总是可以从消息中较早的字段确定。如果指定了 c,则 为该字段的精确值。例如,Byte2 表示值未指定的 2 个字节,Byte1('\n')表示值为'\n'的 1 个字节。

除了以上四种数据类型,其他数据类型在 PostgreSQL Protocol v3.0 的消息中均不支持。

三、消息传输的格式和格式码

在 Postgresql Protocole 中,特定数据类型的数据可以用几种不同格式中的任何一种传输。

从 PostgreSQL 7.4(PostgreSQL Protocol v3.0)开始,协议支持的数据传输支持的格式是 text(文本)和 binary(二进制),该协议为将来的扩展做好了准备。任何值传输的格式由格式代码指定。

客户端可以为每个传输的参数值和查询结果的每一列指定格式代码。text 的格式代码为 0binary 的格式代码是 1,所有其他格式代码都保留以供将来定义。

值的 text 表示是输入/输出转换函数为特定数据类型生成/接受的字符串。在传输 text 的表示中,没有结尾空字符;如果客户端想要将接收到的值作为 C 风格字符串处理,则客户端必须自行将其加 1 个空字符。需要注意的是,text 传输格式的值不允许内嵌空字符

整数的 binary 表示使用网络字节顺序(最高有效字节优先,MSB)。

值得特别注意的是,复杂数据类型的 binary 表示可能会在服务器版本之间发生变化;因此 text 格式通常是更便携更通用的选择。

四、消息的交互流程

PostgreSQL Protocol v3.0 的交互流程主要包括以下几种流程:

1. 启动流程

要开始会话,客户端将打开与服务器的连接并发送启动消息 StartupMessage。启动消息包括用户的名称、用户想要连接到的数据库的名称和要使用的特定协议版本(启动消息可以包括运行时参数的其他设置,但是这些参数都是可选的)。

接着,服务器使用这些信息及其配置文件(如 pg_hba.conf)的内容来确定连接是否暂时可接受,以及需要什么附加身份验证(如果有的话)。

然后,服务器发送适当的身份验证请求消息,客户端必须用适当的身份认证响应消息(如密码)回复该消息。

对于除 GSSAPI、SSPI 和 SASL 之外的所有身份验证方法,最多只有一个请求和一个响应。在某些方法中,客户端不需要响应,因此不会发生身份验证请求。对于 GSSAPI、SSPI 和 SASL,可能需要多次交换数据包才能完成身份验证。

2. 简单查询流程

一个简单查询的周期由客户端端向服务器端发送查询消息来启动。该消息包括一个以文本字符串表示的 SQL 命令。然后,服务器根据查询命令字符串的内容进行执行,执行完成发送一条或多条响应消息,最后发送 ReadyForQuery 响应消息。

ReadyForQuery 通知客户端,可以安全地发送新命令。(客户端实际上不需要在发出另一个命令之前等待 ReadyForQuery,但客户端必须负责弄清楚如果前一个命令失败,而已经发出的后一个命令成功,会发生什么情况。因此,建议的做法是客户端接收到 ReadyForQuery 消息之后再发送新命令。)

简单查询的交互流程中,也会出现一些异常情况,会得到异常的响应。例如,查询 SQL 为空字符串,则响应为 EmptyQueryResponse,后跟 ReadyForQuery。发生错误时,发出 ErrorResponse,然后发出 ReadyForQuery。ErrorResponse 会中止对查询字符串的所有进一步处理。

3. 扩展查询

扩展查询协议将上述简单查询协议分解为多个步骤。为了提高效率,可以多次重复使用 Prepare 步骤的结果。

此外,还提供了其他功能,例如可以将数据值作为单独的参数提供,而不必将它们直接插入到查询字符串中。扩展查询一般需要经过 Parse, Bind 和 Execute 步骤,中间有一些可选步骤如 Describe,Close 和 Flush。

4. Pipelining

扩展查询协议的使用允许流水线,这意味着发送一系列查询而无需等待较早的查询完成。流水线减少了完成给定系列操作所需的网络往返次数。

但是,如果其中一个步骤失败,用户必须仔细考虑所需的处理,因为稍后的查询已经在发送到服务器。

5. 函数调用(Function Call)流程

函数调用(Function Call)子协议允许客户端请求直接调用数据库的 pg_proc 系统目录中存在的任何函数。客户端必须具有函数的执行权限。

函数调用子协议是一个较早版本的遗留功能,在新代码/新版本中最好避免使用。类似的结果可以通过设置执行 SELECT function($1, …)的准备语句的值来实现。然后可以用 Bind/Execute 代替函数调用周期。

函数调用周期由客户端向端发送 FunctionCall 消息来启动。服务端根据函数调用的结果发送一条或多条响应消息,最后发送 ReadyForQuery 响应消息。ReadyForQuery 通知客户端它可以安全地发送新的查询或函数调用。

6. 取消执行中的请求流程

在处理查询期间,客户端可能会请求取消查询。出于实现效率的原因,取消请求不会直接通过正在执行查询的连接发送到服务端:不希望服务端在查询处理过程中不断检查来自客户端的新输入。取消请求应该是相对较少的,所以我们让取消流程稍微麻烦一些,以避免在正常情况下发生错误。

要发出取消请求,客户端应该打开到服务器的新连接并发送一条 CancelRequest 消息,而不是通常通过新连接发送的 StartupMessage 消息。服务器将处理此请求,然后关闭连接。出于安全原因,不直接回复取消请求消息。

7. 结束流程

正常、友好的终止过程是客户端发送 Terminate 终止消息并立即关闭连接。服务端收到此 Terminate 消息后,关闭连接并终止。

在极少数情况下(如管理员通过命令关闭数据库),服务器端可能会在没有任何客户端请求的情况下断开连接。在这种情况下,服务器端将尝试在关闭连接之前发送错误或通知消息,给出断开连接的原因。

8. COPY 操作

COPY 命令允许客户端与服务器之间进行高速批量数据传输。COPY IN 和 COPY OUT 操作都会将连接切换到不同的子协议中,该子协议将持续到操作完成。

COPY IN 是将数据从客户端传输到服务器端,COPY OUT 是将数据从服务器端传输到客户端。还有另一种与 COPY 相关的模式,称为“双向复制”,它允许客户端与服务器之间的双向高速批量数据传输。

9. 异步操作

有几种情况下,服务器端将向客户端发送客户端命令没有特别请求的消息。客户端必须随时准备好处理这些消息,即使这些消息不是为了响应查询请求。

因此,客户端在开始读取查询响应之前,至少应该检查这些情况。服务端异步发送给客户端的消息主要有两种类型:NoticeResponse 消息和 ParameterStatus 消息。

五、错误消息和通知消息

错误消息 ErrorResponse 和通知消息 NoticeResponse,通常是在服务器端处理失败或者发生异常场景时,通知客户端执行失败或者服务器端异常原因的消息。

错误消息和通知消息中可能出现的每个字段类型都有一个单字节标识,并且任何给定的字段类型在每条消息中最多出现一次。错误消息和通知消息中可能出现的字段及其含义如下表所示。

客户端负责格式化显示错误消息和通知消息的信息以满足其需要。客户端应该根据需要进行换行等,错误消息字段中出现的换行符应视为段落分隔符,而不是换行符。

六、其他子协议简介

1. 流复制协议(Streaming Replication Protocol)和逻辑流复制协议(Logical Streaming Replication Protocol)

要启动流复制,客户端在启动消息中发送 replication 参数。replication 参数为布尔值,值为 true(或 on,yes,1)告诉服务器端进入物理复制 walsender 模式,其中可以发出一组复制命令,而不是 SQL 语句。

将 database 作为 replication 参数的值传递,指示服务器端进入逻辑复制 walsender 模式,连接到 dbname 参数中指定的数据库。在逻辑复制 walsender 模式下,可以发出复制命令以及正常的 SQL 命令。在物理复制或逻辑复制 walsender 模式中,只能使用简单的查询协议。

这两种协议,主要应用于主备服务器数据同步的场景。流复制也叫物理复制,是基于对文件块的流复制,逻辑复制是基于对数据元组按照一定格式进行复制。

2. 加密协议

(1)SSL 会话加密

如果 PostgreSQL 的构建选项使用了 SSL,那么客户端和服务器端通信可以使用 SSL 加密。SSL 会话加密在攻击者可能能够捕获会话流量的环境中提供了通信安全性。

(2)GSSAPI 会话加密

如果 PostgreSQL 的构建选项是使用了 GSSAPI,则可以使用 GSSAPI 加密客户端和服务器端的通信流量。这在攻击者可能能够捕获会话流量的环境中提供了通信安全性。

本篇技术贴中第四、五、六章节的相应内容,会在后续推出的《深度解析 PostgreSQL Protocol v3.0》系列文章中为大家进行详细展开,对本系列感兴趣的小伙伴欢迎关注我们,第一时间获取更新内容噢。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频剪辑音效处理软件有哪些?视频剪辑软件那个好用

音效是视频剪辑的重要部分,能起到画龙点睛的作用。在短视频平台中,一段出彩的音效能将原本平平无奇的视频变得生动有趣。那么,视频剪辑音效处理软件有哪些?本文会给大家介绍好用的音效处理软件,同时也会介绍视频剪辑音…

【Pandas 入门-5】Pandas 画图

Pandas 画图 除了结合 matplotlib 与 seaborn 画图外,Pandas 也有自己的画图函数plot,它的语法一般为: DataFrame.plot(xNone,yNone, kindline,subplotsFalse, titleNone)x横坐标数据y纵坐标数据kind默认是线图,还可以是‘bar’…

深入理解 JVM 之——动手编译 JDK

更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 本篇为深入理解 Java 虚拟机第一章的实战内容,推荐在学习前先掌握基础的 Linux 操作、编译原理基础以及扎实的 C/C 功底。 该系列的 GitHub 仓库:https://github.com/Doge2077/lear…

C语言 - 结构体、结构体数组、结构体指针和结构体嵌套

结构体的意义 问题:学籍管理需要每个学生的下列数据:学号、姓名、性别、年龄、分数,请用 C 语言程序存储并处理一组学生的学籍。 单个学生学籍的数据结构: 学号(num): int 型姓名(…

常见网络通信协议(http、https、ws)及安全协议(SSL、TLS、XTLS)

文章内容删除了一大半不合适的内容,发不出来,你懂得。🥰 一、常见网络通信协议1.1、HTTP 协议1.11 HTTP 协议简介1.12 HTTP 协议的工作流程1.13 HTTP 协议的常用方法1.14 HTTP 协议的常见状态码1.15 HTTP 的缺点 1.2 HTTPS 协议1.21 HTTPS 协…

结合OB Cloud区别于MySQL的4大特性,规划降本方案

任何一家企业想要获得持续性的发展与盈利,“降本增效”都是难以绕开的命题。但是“一刀切”的降本影响往往不太可控,成本的快速收缩往往会给业务带来低效运营和增长缓慢的风险。所以我们所说的降本,是指在成本降低的同时,效率不降…

js 正则表达式 验证 :页面中一个输入框,可输入1个或多个vid/pid,使用英文逗号隔开...

就是意思一个输入框里面&#xff0c;按VID/PID格式输入,VID和PID最大长度是4,最多50组 1、页面代码 <el-form ref"ruleForm" :model"tempSet" :rules"rules" label-position"right"> <!-- 最多 50组&#xff0c;每组9个字符…

【C语言】字符函数,字符串函数,内存函数

大家好&#xff01;今天我们来学习C语言中的字符函数&#xff0c;字符串函数和内存函数。 目录 1. 字符函数 1.1 字符分类函数 1.2 字符转换函数 1.2.1 tolower&#xff08;将大写字母转化为小写字母&#xff09; 1.2.2 toupper&#xff08;将小写字母转化为大写字母&…

ZigBee案例笔记 -- RFID卡片读写(模拟饭卡)

RFID模拟饭卡应用 RFID&#xff08;射频识别技术&#xff09;RFID通讯协议RFID发展历史RFID操作流程说明RFID卡片读写流程RFID寻卡RFID防碰撞RFID选卡RFID卡密验证RFID读卡RFID写卡读写数据流程 RFID饭卡模拟案例驱动代码串口协议饭卡操作案例结果优化建议 RFID&#xff08;射频…

Cordova Android 生成的 APK 中添加代码混淆

要在 Cordova Android 生成的 APK 中添加代码混淆&#xff0c;你可以按照以下步骤进行操作&#xff1a; 1. 在项目根目录下&#xff0c;找到 platforms/android/ 目录&#xff0c;进入该目录。 2. 打开 build.gradle 文件&#xff0c;并在 android { ... } 部分添加以下代码&…

关于两个不同数据库的两张表建立数据库链接,关联查询数据

一、数据库链接 数据库链接&#xff08;database link&#xff09;是用于跨不同数据库之间进行连接和数据传输的工具或方法。它允许在一个数据库中访问另一个数据库中的对象和数据。 二、具体操作 以Oracle数据库为例 --1.建立链接tjpt CREATE DATABASE LINK tjpt CONNECT…

go语言--锁

锁的基础&#xff0c;go的锁是构建在原子操作和信号锁之上的 原子锁 原子包实现协程的对同一个数据的操作&#xff0c;可以实现原子操作&#xff0c;只能用于简单变量的简单操作&#xff0c;可以把多个操作变成一个操作 sema锁 也叫信号量锁/信号锁 核心是一个uint32值&#…

基于单片机的串行通信发射机设计

一、项目介绍 串行通信是一种常见的数据传输方式&#xff0c;允许将数据以比特流的形式在发送端和接收端之间传输。当前实现基于STC89C52单片机的串行通信发射机&#xff0c;通过红外发射管和接收头实现自定义协议的数据无线传输。 二、系统设计 2.1 单片机选择 在本设计中&…

前端基础2——CSS样式

文章目录 一、使用方式1.1 内联方式1.2 内部方式1.3 外部导入方式&#xff08;推荐&#xff09; 二、选择器类型2.1 元素选择器2.2 ID选择器2.3 类选择器2.4 派生选择器 三、常用属性3.1 内边距和外边距3.2 文本3.3 边框3.4 背景3.5 定位3.6 浮动3.7 字体3.8 其他属性 四、案例…

MySQL 数据库常用命令大全(完整版)

文章目录 1. MySQL命令2. MySQL基础命令3. MySQL命令简介4. MySQL常用命令4.1 MySQL准备篇4.1.1 启动和停止MySQL服务4.1.2 修改MySQL账户密码4.1.3 MySQL的登陆和退出4.1.4 查看MySQL版本 4.2 DDL篇&#xff08;数据定义&#xff09;4.2.1 查询数据库4.2.2 创建数据库4.2.3 使…

DataTable扩展 列转行方法(2*2矩阵转换)

源数据 如图所示 // <summary>/// DataTable扩展 列转行方法&#xff08;2*2矩阵转换&#xff09;/// </summary>/// <param name"dtSource">数据源</param>/// <param name"columnFilter">逗号分隔 如SDateTime,PM25,PM10…

docker desktop安装es 并连接elasticsearch-head:5

首先要保证docker安装成功&#xff0c;打开cmd&#xff0c;输入docker -v&#xff0c;出现如下界面说明安装成功了 下面开始安装es 第一步&#xff1a;拉取es镜像 docker pull elasticsearch:7.6.2第二步&#xff1a;运行容器 docker run -d --namees7 --restartalways -p 9…

ShardingSphere——压测实战

摘要 Apache ShardingSphere 关注于全链路压测场景下&#xff0c;数据库层面的解决方案。 将压测数据自动路由至用户指定的数据库&#xff0c;是 Apache ShardingSphere 影子库模块的主要设计目标。 一、压测背景 在基于微服务的分布式应用架构下&#xff0c;业务需要多个服…

WebVR — 网络虚拟现实

推荐&#xff1a;使用 NSDT编辑器 快速搭建3D应用场景 虚拟现实设备 随着Oculus Rift和许多其他生产设备即将上市&#xff0c;未来看起来很光明——我们已经有足够的技术来使VR体验“足够好”&#xff0c;可以玩游戏。有许多设备可供选择&#xff1a;像Oculus Rift或HTC Vive这…

JasperReport定义变量后打印PDF变量为null以及整个pdf文件为空白

问题1: JasperReport打印出来的整个pdf文件为空白文件&#xff1b; 问题2&#xff1a;JasperReport定义变量后打印PDF变量为null&#xff1b; 问题1原因是因为缺少数据源JRDataSource JasperFillManager.fillReport(jasperReport, params,new JREmptyDataSource());如果你打印…