> For the complete documentation index, see [llms.txt](https://litedb.gitbook.io/litedb-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://litedb.gitbook.io/litedb-docs/architecture_and_design/network_protocol/message_protocol.md).

# 消息协议

LiteDB v1 使用大端二进制编码。每个消息 Payload 都由帧头中的 `Kind` 决定 Schema；固定 32 字节帧头及 TCP 读取方式见[传输协议](/litedb-docs/architecture_and_design/network_protocol/transport_protocol.md)。旧版 16 字节头、连续 `1..5` 消息编号和 `Payload Size` 字段不再适用。

## 帧格式摘要

```txt
u32 magic        = 0x4C444250  # "LDBP"
u32 frame_size                 # 32 + payload bytes
u16 version      = 1
u16 header_size  = 32
u16 kind
u16 flags        = 0
u64 request_id
u64 reserved     = 0
bytes payload
```

`frame_size` 是整帧长度。已知消息编号是稀疏枚举，不能用数值范围判断是否合法。v1 解码器会拒绝未知 `Kind`、非零 `Flags`、非零 `Reserved`、错误 Magic、错误版本和错误头长。

## 通用编码约定

### 基本类型

| 记法                    | 编码                                  |
| --------------------- | ----------------------------------- |
| `u8`                  | 1 字节无符号整数                           |
| `u16` / `u32` / `u64` | 2 / 4 / 8 字节大端无符号整数                 |
| `i32` / `i64`         | 4 / 8 字节大端有符号整数位模式                  |
| `f32` / `f64`         | IEEE 754 位模式，分别按大端 `u32` / `u64` 传输 |
| `bytes[N]`            | 恰好 N 个原始字节                          |

### 字符串

```txt
u32 byte_length
bytes[byte_length]
```

字符串没有 NUL 终止符，长度按字节计算。当前实现把内容存入 `std::string`，协议解码器不验证 UTF-8 或其他字符集；跨语言客户端应自行统一文本编码。

### 布尔与可选字段

布尔值和“是否存在”标记都使用 `u8`：`0` 表示 false / 不存在，`1` 表示 true / 存在。其他值会被视为非法 Payload。

### 计数

列数、行数、每行值数和向量元素数都使用大端 `u32`。解码器会在分配容器前检查配置上限与剩余 Payload 长度。

## 消息类型

|     Kind | 名称                   | 方向              | Payload    |
| -------: | -------------------- | --------------- | ---------- |
| `0x0001` | `HelloRequest`       | Client → Server | 客户端支持的版本范围 |
| `0x0002` | `HelloResponse`      | Server → Client | 服务端选定版本    |
| `0x0003` | `CloseRequest`       | Client → Server | 空          |
| `0x0100` | `ExecuteSqlRequest`  | Client → Server | SQL 字符串    |
| `0x0101` | `ExecuteSqlResponse` | Server → Client | 执行结果       |
| `0x0102` | `CancelRequest`      | Client → Server | 当前未定义      |
| `0x0200` | `PingRequest`        | Client → Server | 空          |
| `0x0201` | `PongResponse`       | Server → Client | 空          |
| `0xFFFF` | `ErrorResponse`      | Server → Client | 16 位错误码与消息 |

### HelloRequest

```txt
u16 min_version
u16 max_version
```

版本范围必须满足 `min_version <= max_version`。当前服务端只支持 v1，因此范围必须包含 `1`。`HelloRequest` 必须是连接建立后的第一帧；成功时服务端返回相同 `Request ID` 的 `HelloResponse`。

需要注意，帧头本身的 `Version` 在解析 Hello Payload 前就必须为 `1`。当前握手只在 v1 帧格式内确认双方支持范围，不提供旧帧格式兼容。

### HelloResponse

```txt
u16 selected_version  # 当前固定为 1
```

客户端收到其他版本会返回 `UnsupportedVersion`。

### CloseRequest

Payload 必须为空。服务端收到后结束连接，不返回 `CloseResponse`。若 Payload 非空，服务端会尽力发送 `InvalidPayload` 错误，然后仍然关闭连接。

### ExecuteSqlRequest

```txt
string sql
```

SQL 文本使用通用长度前缀字符串格式。默认编码和解码上限都是 1 MiB。Payload 中出现 SQL 字符串之后的多余字节会导致解码失败。

### ExecuteSqlResponse

```txt
u8  result_kind
u64 affected_rows
u8  has_selected_database_name
[if has_selected_database_name == 1]
    string selected_database_name

u32 column_count
repeat column_count times:
    string column_name
    LogicalType column_type

u32 row_count
repeat row_count times:
    u32 value_count
    repeat value_count times:
        Value value
```

`result_kind`：

|   值 | 名称            | 含义              |
| --: | ------------- | --------------- |
| `0` | `Command`     | DDL / DML 等命令结果 |
| `1` | `RowSet`      | 查询结果集           |
| `2` | `UseDatabase` | 切换当前数据库         |

线上只传输 `selected_database_name`，不会暴露内部数据库 ID。每行的 `value_count` 必须等于 `column_count`，并且不能超过每行值数上限；否则整条响应为非法 Payload。

整个执行结果会物化并编码进一个 Payload。当前没有分页、结果分片或延迟取数协议。

#### LogicalType

```txt
u8 type_id
u8 has_parameter
[if has_parameter == 1]
    u64 parameter
```

| `type_id` | 类型        |
| --------: | --------- |
|       `0` | `NULL`    |
|       `1` | `BOOLEAN` |
|       `2` | `INTEGER` |
|       `3` | `BIGINT`  |
|       `4` | `FLOAT`   |
|       `5` | `DOUBLE`  |
|       `6` | `VARCHAR` |
|       `7` | `VECTOR`  |

`parameter` 通常表示 `VARCHAR(n)` 的长度或 `VECTOR(n)` 的维度。当前 codec 只校验 `type_id` 与可选标记是否合法，不限制参数只能出现在哪些逻辑类型上。

#### Value

每个值先写入一个 `u8` 类型 tag，再按 tag 写正文：

| Tag | 类型        | 正文                             |
| --: | --------- | ------------------------------ |
| `0` | `NULL`    | 无                              |
| `1` | `BOOLEAN` | `u8`，只能为 `0` 或 `1`             |
| `2` | `INTEGER` | `i32`                          |
| `3` | `BIGINT`  | `i64`                          |
| `4` | `FLOAT`   | `f32`                          |
| `5` | `DOUBLE`  | `f64`                          |
| `6` | `VARCHAR` | `string`                       |
| `7` | `VECTOR`  | `u32 count`，随后 `count` 个 `f64` |

行宽会与列数核对，但当前解码器尚未逐列验证 Value tag 是否与该列声明的 `LogicalType` 一致。接入不可信客户端时，不应把列类型匹配当作 codec 已经保证的条件。

### CancelRequest

`CancelRequest` 的编号已经保留，但当前没有对应的 Payload codec，也没有正在执行请求的取消机制。服务端收到后固定返回同 `Request ID` 的 `ErrorResponse`，错误为 `UnsupportedMessage`，随后仍可继续使用连接。

### PingRequest / PongResponse

两者 Payload 都必须为空。握手完成后，服务端收到 `PingRequest` 会返回同 `Request ID` 的 `PongResponse`。Ping Payload 非空时，服务端返回 `InvalidPayload` 并关闭连接。

### ErrorResponse

```txt
u16 code
string message
```

`code` 是本地 `Error::encode_code()` 的线格式：

```txt
code = (error_category << 8) | local_error_code
```

高 8 位是错误类别，低 8 位是该类别内的错误编号。SQL 执行错误会保留其原始类别和编号；协议解码错误也可能来自底层 IO 类别，而不一定属于 Protocol。客户端应至少保留完整 16 位值，不应假设它是简单的连续协议错误枚举。

错误消息是边界处可展示的文本。错误对象的模块上下文、系统 `error_code` 和 cause 链不会通过网络传输。官方客户端收到 `ErrorResponse` 后会把它转换成 `ServerError`，同时在上下文中保留服务端 16 位 code。

Protocol 类别的高字节为 `0x11`，当前本地错误编号如下：

| 低 8 位 | 名称                      | 典型含义            |
| ----: | ----------------------- | --------------- |
|   `0` | `UnexpectedEnd`         | 协议数据意外结束        |
|   `1` | `InvalidMagic`          | Magic 不匹配       |
|   `2` | `UnsupportedVersion`    | 不支持的版本          |
|   `3` | `InvalidHeaderSize`     | 头长不是 32         |
|   `4` | `InvalidFrameSize`      | 整帧长度非法          |
|   `5` | `FrameTooLarge`         | 超过帧大小限制         |
|   `6` | `InvalidMessageKind`    | 未知消息编号          |
|   `7` | `InvalidFlags`          | v1 Flags 非零     |
|   `8` | `InvalidReservedField`  | Reserved 非零     |
|   `9` | `InvalidPayload`        | Payload 字段或结构非法 |
|  `10` | `ResourceLimitExceeded` | 超出消息资源限制        |
|  `11` | `HandshakeRequired`     | 握手前发送了其他消息      |
|  `12` | `UnexpectedMessage`     | 当前连接状态不接受该方向或类型 |
|  `13` | `UnsupportedMessage`    | 消息已识别但功能未实现     |

帧头损坏时，服务端通常无法安全构造错误响应，会直接断开连接。因此，表中的帧级错误并不保证在线上以 `ErrorResponse` 返回。

## Request ID

* 官方客户端从 `1` 开始为每个请求单调递增分配 `u64` ID；
* `HelloResponse`、SQL 响应、Pong 和 Error 都回显请求的 ID；
* 客户端的 `roundtrip` 会拒绝 ID 不匹配的响应；
* `CloseRequest` 也带 ID，但没有响应；
* ID 字段为未来关联能力保留，但当前单连接没有多个并发在途请求。

## 默认资源限制

| 限制         |           默认值 | 作用位置                         |
| ---------- | ------------: | ---------------------------- |
| 整帧大小       |        16 MiB | 帧 codec 硬上限；包含 32 字节头        |
| Payload 大小 | 16 MiB - 32 B | 由整帧硬上限推导                     |
| 单个普通字符串    |         1 MiB | 数据库名、列名、字符串值、错误消息等           |
| SQL 字符串    |         1 MiB | `ExecuteSqlRequest`          |
| 列数         |          4096 | `ExecuteSqlResponse`         |
| 行数         |         65536 | `ExecuteSqlResponse`         |
| 每行值数       |          4096 | `ExecuteSqlResponse`         |
| 向量元素数      |       2097148 | 由最大 Payload 除以 8 得到的理论默认解码预算 |

向量本身还需要 tag 和 count 等字段，因此单个实际可编码向量达不到上述理论元素数；最终仍以完整 Payload 能否装入一帧为准。

服务端可以通过 `ServerConfig.decode_limits` 调整消息解码上限，通过 `ServerConfig.max_frame_size` 调低整帧上限；任何配置都不能突破 codec 的 16 MiB 帧硬限制。编码端仍按协议默认上限检查，并最终受单帧上限约束。

所有已实现的 Payload 解码器都要求恰好消费完整 Payload，不接受尾随字节。字符串长度、集合计数、向量长度、布尔标记、可选标记和行宽都会在构造大对象或返回结果前校验。

## 线格式示例

### Hello 握手请求

客户端支持范围 `[1, 1]`，`Request ID = 1`：

```txt
Frame Size  = 32 + 4 = 36 (0x00000024)
Kind        = 0x0001 (HelloRequest)
Request ID  = 1
Payload     = u16(1) + u16(1)

4c444250 00000024 0001 0020 0001 0000
0000000000000001 0000000000000000 0001 0001
```

### Ping 请求

空 Payload、`Request ID = 42` 的完整帧正好为 32 字节：

```txt
4c444250 00000020 0001 0020 0200 0000
000000000000002a 0000000000000000
```

### SQL 请求

SQL 为 ASCII `SELECT 1` 时正文为 8 字节：

```txt
Payload    = u32(8) + bytes("SELECT 1")
Payload Size = 12 bytes
Frame Size   = 32 + 12 = 44 bytes
Kind         = 0x0100 (ExecuteSqlRequest)
```

注意，线上头部只有 `Frame Size`，没有独立的 `Payload Size` 字段；上面的 `Payload Size` 只是由 `Frame Size - 32` 推导出的说明值。

## 当前协议边界

* v1 没有认证、压缩、校验和、分片或扩展 Flags；
* 字符串没有字符集标记，也不验证 UTF-8；
* SQL 结果必须放入一个 Payload，没有游标或分页消息；
* Value tag 尚未与对应列类型交叉校验；
* `CancelRequest` 尚不支持；
* `CloseRequest` 没有确认响应；
* 版本、头长和消息集合都是 v1 固定合同，没有旧线格式兼容层。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://litedb.gitbook.io/litedb-docs/architecture_and_design/network_protocol/message_protocol.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
