diff --git a/.github/instructions/benchmark.instructions.md b/.github/instructions/benchmark.instructions.md
new file mode 100644
index 0000000..56ffc19
--- /dev/null
+++ b/.github/instructions/benchmark.instructions.md
@@ -0,0 +1,77 @@
+---
+applyTo: "**/Benchmark/**"
+---
+
+# 性能测试指令
+
+适用于性能测试、压力测试、基准测试、BenchmarkDotNet 相关任务。
+
+---
+
+## 1. 项目结构
+
+- 基准测试统一放在 `Benchmark/` 项目,按主题分子目录(如 `PacketBenchmarks/`、`CacheBenchmarks/`)
+- 入口 `Program.cs` 使用 `BenchmarkSwitcher` 模式,**不要修改**
+- TFM 使用最新稳定版,`<LangVersion>latest</LangVersion>`
+
+## 2. 代码规范
+
+遵循主指令全部编码规范(类型名用 `String`/`Int32` 等、file-scoped namespace),另有以下补充:
+
+- **命名空间**:`Benchmark.{主题}Benchmarks`
+- **类名**:`{被测类型}Benchmark` 或 `{被测主题}Benchmark`
+- **必须标注** `[MemoryDiagnoser]` 和 `[SimpleJob]`(需调整迭代次数时用 `[SimpleJob(iterationCount: N)]`)
+- **方法描述**:`[Benchmark(Description = "中文描述")]`,方便报告阅读
+- **参数化**:用 `[Params]` 或 `[ParamsSource]` 控制数据规模
+- **初始化 / 清理**:分别放 `[GlobalSetup]` 和 `[GlobalCleanup]`
+- **分组**:同类测试用 `#region` 分组
+- **多线程并发**:动态线程数包含 CPU 核心数,推荐模板:
+
+```csharp
+public static IEnumerable<Int32> ThreadCounts
+{
+ get
+ {
+ var cores = Environment.ProcessorCount;
+ var set = new SortedSet<Int32> { 1, 4, 8, 32 };
+ set.Add(cores);
+ return set;
+ }
+}
+
+[ParamsSource(nameof(ThreadCounts))]
+public Int32 ThreadCount { get; set; }
+```
+
+## 3. 运行要求
+
+- 必须以 **Release 模式**运行,获取有代表性的峰值数据
+- 运行全部:`dotnet run -c Release`
+- 运行指定类:`dotnet run -c Release -- --filter *ClassName*`
+- ❌ 禁止在 Debug 模式下采集数据写入报告
+
+## 4. 测试维度
+
+- **并发维度**:单线程 + 多线程(多线程含与当前 CPU 核心数相同的并发数)
+- **操作维度**:单一操作 + 批量操作
+
+## 5. 常见错误
+
+- ❌ 在 `[Benchmark]` 方法内做初始化(应放 `[GlobalSetup]`)
+- ❌ 忽略返回值导致 JIT 死码消除(确保返回或赋值给字段)
+- ❌ 手动 `Stopwatch` 计时(BDN 自动处理)
+- ❌ `using` 的 `Dispose` 开销混入测量(仅在测试 Dispose 本身时才包含)
+
+## 6. 报告存放
+
+`Doc/Benchmark/{测试主题}性能测试.md`(UTF-8 无 BOM)
+
+## 7. 报告结构(顺序固定)
+
+1. **性能概览**(放最前:一句话点明被测功能用途 + 简单语言总结核心发现)
+2. 测试环境 → 测试方法 → 测试结果(BDN 原始表格,保留 Mean/Error/StdDev/Allocated)
+3. 核心指标(换算 msg/s、QPS 等业务指标)
+4. **对比分析**
+ - 纵向:同场景不同并发趋势,找出最优并发点
+ - 横向:不同方案同并发差异百分比
+5. 性能瓶颈定位 → 优化建议(按优先级排序,含预期收益)
diff --git a/.github/instructions/development.instructions.md b/.github/instructions/development.instructions.md
new file mode 100644
index 0000000..44729b4
--- /dev/null
+++ b/.github/instructions/development.instructions.md
@@ -0,0 +1,324 @@
+---
+applyTo: "Doc/**"
+---
+
+# AI 辅助开发流程指令
+
+适用于新建应用系统、新增功能模块、需求整理、架构设计等研发全流程任务。
+
+---
+
+## 1. 流程总览
+
+```
+需求整理 → 需求评审与拆分 → 技术方案设计 → 任务分解 → 迭代开发 → 集成验证 → 验收回顾
+```
+
+**核心原则**:大需求必须拆小,每个迭代交付可验证的最小功能单元。禁止"一次性全做完"。
+
+---
+
+## 2. 各阶段规范
+
+### 2.1 需求整理
+
+用户提供原始描述(口语化、列表、草稿均可),AI 整理为以下结构(需求 + 功能清单 + 验收 合为一个文件):
+
+```markdown
+# {项目/模块名}需求
+
+## 1. 背景与目标
+- 为什么做(痛点/动机)
+- 做到什么程度算成功(可衡量目标)
+
+## 2. 用户角色
+| 角色 | 说明 | 核心诉求 |
+|------|------|---------|
+
+## 3. 功能需求
+### 3.1 {功能模块名}
+- **描述**:一句话说明
+- **用户故事**:作为{角色},我希望{操作},以便{价值}
+- **验收条件**(AC):
+ - [ ] 条件 1
+ - [ ] 条件 2
+- **优先级**:Must / Should / Could / Won't
+
+## 4. 非功能需求
+- 性能 / 安全 / 兼容性(三项必填)
+
+## 5. 边界与约束
+- 不做什么(明确排除项)
+- 已知限制 / 技术债务
+
+## 6. 功能清单与迭代计划
+(需求评审拆分后填写,见 2.2)
+
+## 7. 验收记录
+(开发完成后填写,见 2.7)
+
+## 8. 术语表
+| 术语 | 定义 |
+|------|------|
+```
+
+**规则**:每个功能必须有 AC,无 AC 不可进入开发;优先级用 MoSCoW 四级;非功能需求至少覆盖性能、安全、兼容性。
+
+### 2.2 需求评审与拆分
+
+按**纵向切片**(端到端功能,非技术层)拆分,遵循 INVEST 原则,单个功能单元 ≤ 1-2 天工作量,有依赖须标注。
+
+写入需求文档「6. 功能清单与迭代计划」:
+
+```markdown
+## 6. 功能清单与迭代计划
+
+### 迭代 1:{主题}(Must 级别)
+| 编号 | 功能点 | 验收条件 | 前置依赖 | 预估工作量 |
+|------|--------|---------|---------|----------|
+| F001 | xxx | AC1, AC2 | 无 | 0.5d |
+| F002 | xxx | AC1 | F001 | 1d |
+
+### 迭代 2:{主题}(Should 级别)
+...
+```
+
+### 2.3 技术方案设计
+
+```markdown
+# {项目/模块名}架构
+
+## 1. 架构概览
+## 2. 数据模型
+## 3. 接口设计
+| 接口 | 方法 | 路径/签名 | 入参 | 出参 | 说明 |
+|------|------|----------|------|------|------|
+## 4. 技术选型
+| 领域 | 选型 | 理由 |
+|------|------|------|
+## 5. 关键设计决策
+| 决策点 | 方案 | 备选方案 | 选择理由 |
+|--------|------|---------|---------|
+## 6. 任务分解
+(见 2.4)
+## 7. 风险与缓解
+| 风险 | 影响 | 缓解措施 |
+|------|------|---------|
+```
+
+**规则**:优先使用 NewLife 已有组件(XCode、Remoting、Stardust 等);数据模型考虑 XCode 实体规范;接口遵循现有 API 风格。
+
+### 2.4 任务分解
+
+单个任务 = 一次 AI 对话可完成的工作量(编码 + 测试 + 自测通过)。写入技术方案「6. 任务分解」:
+
+```markdown
+### 任务 T001:{动词 + 目标}
+- **对应功能**:F001
+- **输入**:前置条件 / 已有代码
+- **产出**:新增/修改哪些文件
+- **验收**:怎样算完成
+```
+
+**批次编排**(用于自治模式,见第 6 节):按依赖关系编排为批次,每批次 5-8 个任务,同批次内尽量无相互依赖,基础设施任务排在前面,每批次结束设 `[检查点 N]`,标注本批次产出是下批次哪些输入。
+
+### 2.5 迭代开发
+
+流程:`理解任务 → 检索现有实现 → 编码 → 编译通过 → 测试通过 → 提交说明`
+
+- 严格遵守主指令编码规范,每个任务必须编译通过
+- 常规模式:遇歧义暂停确认;自治模式:记录跳过继续(见第 6 节)
+- 有依赖按顺序执行,不跳跃
+
+### 2.6 集成验证
+
+全部编译通过 → 单元测试通过 → 端到端主流程走通 → 异常场景覆盖 → 性能符合预期
+
+### 2.7 验收与回顾
+
+对照需求文档逐条验收,写入「7. 验收记录」:
+
+```markdown
+## 7. 验收记录
+
+### 功能验收
+| 编号 | 功能点 | 验收条件 | 状态 | 备注 |
+|------|--------|---------|------|------|
+
+### 遗留问题
+| 问题 | 影响 | 后续计划 |
+|------|------|---------|
+
+### 经验总结
+- 做得好的 / 待改进的
+```
+
+---
+
+## 3. 文档存放规范
+
+全流程仅产出 **2 个文档**,扁平存放在 `Doc/` 下:
+
+| 文档 | 文件名 | 包含内容 |
+|------|--------|--------|
+| 需求文档 | `Doc/{项目名}需求.md` | 背景目标 + 功能需求 + 功能清单 + 验收记录 + 术语表 |
+| 技术方案 | `Doc/{项目名}架构.md` | 架构 + 数据模型 + 接口 + 技术选型 + 任务分解 + 风险 |
+
+UTF-8 无 BOM;已有文件必须先读取再增量修改,禁止覆盖;各阶段产出追加到对应章节,不新建文件。
+
+---
+
+## 4. AI 协作要点
+
+### 4.1 阶段切换
+
+| 用户说 | 进入阶段 |
+|--------|---------|
+| "整理需求"/"写需求" | 2.1 需求整理 |
+| "拆分"/"拆解"/"排优先级" | 2.2 需求评审与拆分 |
+| "技术方案"/"架构设计"/"怎么实现" | 2.3 技术方案设计 |
+| "开始开发"/"写代码"/"实现 F001" | 2.5 迭代开发 |
+| "全部搞完"/"批量开发"/"自治模式"/"一次性做完"/"继续处理"/"接着做" | 第 6 节自治批处理 |
+| "验收"/"检查完成情况" | 2.7 验收与回顾 |
+| 一大段描述未指定阶段 | 默认 2.1 需求整理 |
+
+### 4.2 主动引导
+
+每阶段完成后提示下一步:需求整理完 → 拆分? → 技术方案? → 任务分解 → 开发?
+
+### 4.3 大需求防护
+
+功能点 > 5 / 实体 > 3 / 跨 2 层以上 / 描述 > 500 字 → 必须先拆分再开发。
+
+---
+
+## 5. 常见反模式(禁止)
+
+- ❌ 跳过需求直接编码
+- ❌ 一次性输出所有代码(大需求必须拆迭代或使用自治模式)
+- ❌ 需求文档没有验收条件
+- ❌ 功能拆分按技术层而非用户价值
+- ❌ 任务没有完成标准就开始编码
+- ❌ 完成后不做验收对照
+- ❌ 自治模式下遇阻塞问题死等用户(应记录跳过,继续后续)
+- ❌ 自治模式下做需要人工决策的架构变更(应记录待确认,现有方案兜底)
+- ❌ 跨批次不做编译验证
+
+---
+
+## 6. 自治批处理模式
+
+架构师已确认需求和技术方案后,AI 按任务清单自主执行,最小化人工介入。
+
+### 6.1 进入条件(全部满足)
+
+- [ ] 需求文档已完成且架构师已确认
+- [ ] 技术方案已完成且架构师已确认
+- [ ] 任务已分解并编排为批次
+- [ ] 用户明确触发("全部搞完"/"批量开发"/"自治模式"等)
+
+未满足时提示缺少哪些条件。
+
+### 6.2 计划结构与循环刷新
+
+AI 用 plan 工具创建层次化计划,「前置刷新 + 批次执行」循环:
+
+```
+1. [前置] 读取需求文档与技术方案
+2. [前置] 读取任务清单与进度状态
+3. [前置] 全量编译确认基线
+4. [前置] 识别可并行的批次组
+5. [批次1] 执行 T001-T005(子步骤展开各任务)
+6. [检查点1] 输出批次1报告
+7. [刷新] 重读需求文档与技术方案
+8. [批次2] 执行 T006-T010
+9. [检查点2] 输出批次2报告
+...(循环:刷新 → 批次 → 检查点)
+N-2. [后置] 全量编译与集成验证
+N-1. [后置] 补完被跳过的任务
+N. [后置] 生成验收报告
+```
+
+**要点**:
+- 主步骤 15-25 个(不超过 30),子步骤展开具体任务仅供参考不单独追踪
+- 刷新步骤穿插在每两个批次之间,`get_file` 重读文档对抗上下文漂移
+- 用 `update_plan_progress` 跟踪主步骤,不为每个子任务调用
+- 无依赖的批次可合并为一个主步骤执行,有依赖的必须顺序执行
+
+### 6.3 执行协议
+
+| 情况 | 处理方式 |
+|------|----------|
+| 任务明确无歧义 | 直接执行:编码 → 编译 → 测试 |
+| 小歧义可合理推断 | 执行并在问题日志记录推断依据 |
+| 重大歧义或多种等价方案 | 标记 `⏸️ 待确认`,跳过 |
+| 前置任务被跳过 | 标记 `⏸️ 依赖阻塞:T0xx`,跳过 |
+| 编译失败短时间无法修复 | 回滚改动,记录并跳过 |
+| 涉及公共 API / 架构变更 | 标记 `⏸️ 需架构师决策`,兜底或跳过 |
+
+### 6.4 检查点报告
+
+每批次完毕后输出:
+
+```markdown
+## 检查点 N 报告
+
+### 完成情况
+| 任务 | 状态 | 说明 |
+|------|------|------|
+| T001 | ✅ 完成 | |
+| T003 | ⏸️ 跳过 | 需确认:xxx |
+
+### 编译状态
+- 全量编译:✅ 通过 / ❌ 失败(错误详情)
+
+### 问题日志
+| 编号 | 类型 | 描述 | 影响任务 | 建议方案 |
+|------|------|------|---------|----------|
+
+### 统计
+- 本批次 N 个,完成 X 个,跳过 Y 个
+- 累计进度:已完成 X / 总计 Z(XX%)
+- 上下文预估:{已处理任务数} / {建议上限}
+```
+
+### 6.5 用户回复与继续
+
+架构师回来后:AI 呈现检查点报告 → 架构师批量回复问题("Q001 OK,Q002 选 A")→ AI 修正推断 + 执行跳过的任务 + 继续下批次 → 循环至完成。
+
+触发词:"继续"/"继续处理"/"回复完了"/"接着做"
+
+### 6.6 质量护栏(自动执行)
+
+编译门禁(失败即修复或回滚)/ 命名与技术方案一致 / 编码规范严格遵守 / 新增代码前搜索现有实现避免重复 / 不擅自引入新 NuGet 包
+
+### 6.7 会话边界处理
+
+每个检查点后、连续完成 15+ 任务后、搜索结果不准确时 → 评估是否需要新会话。
+
+**新会话续接模板**:
+
+```
+我们在做 {项目名} 的自治批处理开发。
+- 需求文档:Doc/{项目名}需求.md
+- 技术方案:Doc/{项目名}架构.md
+- 当前进度:批次 N 已完成,从批次 N+1 的 T0xx 开始继续
+- 待解决问题:{问题编号}
+请读取以上文档,从 T0xx 继续执行,自治模式。
+```
+
+上下文即将耗尽时 AI 主动提醒并生成上述模板。新会话前 4 步仍为前置刷新,已完成批次直接标记完成。
+
+### 6.8 批次大小建议
+
+| 复杂度 | 批次大小 |
+|--------|---------|
+| 简单(CRUD) | 8-10 |
+| 中等(业务逻辑) | 5-7 |
+| 复杂(算法、并发) | 3-5 |
+
+单会话上限:3-4 个批次(约 15-25 个任务)。
+
+---
+
+(完)
diff --git a/.github/instructions/net.instructions.md b/.github/instructions/net.instructions.md
new file mode 100644
index 0000000..ba7509b
--- /dev/null
+++ b/.github/instructions/net.instructions.md
@@ -0,0 +1,573 @@
+---
+applyTo: "**/Net/**"
+---
+
+# 网络编程指令
+
+适用于基于 `NewLife.Net` 的网络服务器(`NetServer`)和客户端(`ISocketClient`)开发任务。
+
+---
+
+## 1. 架构概览
+
+NewLife 网络框架分为两层:
+
+| 层级 | 服务端 | 客户端 | 说明 |
+|------|--------|--------|------|
+| **应用层** | `NetServer` / `NetServer<TSession>` | — | 管理监听、会话生命周期、管道 |
+| **传输层** | `TcpServer` / `UdpServer` | `TcpSession` / `UdpServer`(客户端模式) | 底层 Socket 收发 |
+| **会话** | `NetSession` / `NetSession<TServer>` | — | 每个连接对应一个会话,业务逻辑入口 |
+| **管道** | `IPipeline` + `IPipelineHandler` | 同左 | 编解码、粘包拆包、消息匹配 |
+
+**关键接口**:
+- `ISocketClient` — 客户端连接接口(Open/Close/Send/Receive)
+- `ISocketRemote` — 远程通信接口(Send/Receive/SendMessageAsync)
+- `INetSession` — 网络会话接口(服务端每个连接的业务处理单元)
+- `INetHandler` — 网络数据处理器接口(Init/Process)
+
+---
+
+## 2. 服务端开发规范
+
+### 2.1 基本模式
+
+推荐使用泛型 `NetServer<TSession>` + 自定义 `NetSession` 子类:
+
+```csharp
+/// <summary>自定义网络服务器</summary>
+class MyServer : NetServer<MySession> { }
+
+/// <summary>自定义会话,每个客户端连接对应一个实例</summary>
+class MySession : NetSession<MyServer>
+{
+ /// <summary>客户端连接</summary>
+ protected override void OnConnected()
+ {
+ base.OnConnected();
+ WriteLog("客户端已连接 {0}", Remote);
+ }
+
+ /// <summary>收到客户端数据</summary>
+ protected override void OnReceive(ReceivedEventArgs e)
+ {
+ base.OnReceive(e);
+ // 业务处理
+ }
+
+ /// <summary>客户端断开</summary>
+ protected override void OnDisconnected(String reason)
+ {
+ base.OnDisconnected(reason);
+ }
+}
+```
+
+### 2.2 服务器启动配置
+
+```csharp
+var server = new MyServer
+{
+ Port = 8080, // 监听端口,0 表示随机
+ ProtocolType = NetType.Tcp, // Tcp/Udp/Unknown(同时监听)
+ // AddressFamily = AddressFamily.InterNetwork, // 仅IPv4,默认同时IPv4+IPv6
+ ServiceProvider = provider, // 依赖注入
+ Log = XTrace.Log, // 应用日志
+ SessionLog = XTrace.Log, // 会话日志
+ Tracer = tracer, // APM 追踪
+#if DEBUG
+ SocketLog = XTrace.Log, // Socket 层日志(仅调试)
+ LogSend = true,
+ LogReceive = true,
+#endif
+};
+server.Start();
+```
+
+### 2.3 会话生命周期
+
+```
+连接建立 → OnConnected() → OnReceive()... → OnDisconnected(reason) → Dispose()
+```
+
+- **OnConnected**:初始化会话状态、发送欢迎消息
+- **OnReceive**:核心业务处理入口,`e.Packet` 为原始数据,`e.Message` 为管道解码后的消息
+- **OnDisconnected**:清理资源、记录日志,`reason` 包含断开原因
+- 会话内可通过 `ServiceProvider` 获取 Scoped 服务
+
+### 2.4 服务端发送数据
+
+| 方法 | 说明 |
+|------|------|
+| `Send(IPacket)` | 直接发送原始数据,不经过管道 |
+| `Send(String)` | 发送字符串,默认 UTF-8 |
+| `Send(ReadOnlySpan<Byte>)` | 高性能发送 |
+| `SendMessage(Object)` | 通过管道编码后发送,不等待响应 |
+| `SendReply(Object, ReceivedEventArgs)` | 发送响应消息,与请求关联(用于 StandardCodec 等协议) |
+| `SendMessageAsync(Object)` | 通过管道发送并等待响应 |
+
+### 2.5 群发
+
+```csharp
+// 群发数据给所有在线客户端
+await server.SendAllAsync(data);
+
+// 带过滤条件群发
+await server.SendAllAsync(data, session => session.ID > 100);
+
+// 群发管道消息
+server.SendAllMessage(message, session => session["VIP"] is true);
+```
+
+群发要求 `UseSession = true`(默认开启)。
+
+### 2.6 事件模式(简单场景)
+
+不需要自定义会话时,可直接使用事件:
+
+```csharp
+var server = new NetServer { Port = 8080 };
+server.Received += (sender, e) =>
+{
+ if (sender is INetSession session)
+ session.Send(e.Packet); // Echo
+};
+server.Start();
+```
+
+---
+
+## 3. 客户端开发规范
+
+### 3.1 创建客户端
+
+通过 `NetUri.CreateRemote()` 扩展方法创建:
+
+```csharp
+// TCP 客户端
+var client = new NetUri("tcp://127.0.0.1:8080").CreateRemote();
+
+// UDP 客户端
+var client = new NetUri("udp://127.0.0.1:8080").CreateRemote();
+
+// WebSocket 客户端
+var client = new NetUri("ws://127.0.0.1:8080/path").CreateRemote();
+```
+
+`CreateRemote` 根据协议自动返回 `TcpSession` / `UdpServer` / `WebSocketClient`。
+
+### 3.2 客户端使用
+
+```csharp
+var uri = new NetUri("tcp://127.0.0.1:8080");
+var client = uri.CreateRemote();
+client.Log = XTrace.Log;
+client.Open();
+
+// 发送原始数据(不经过管道)
+client.Send("Hello");
+
+// 事件驱动接收
+client.Received += (sender, e) =>
+{
+ // e.Packet 原始数据,e.Message 管道解码后的消息
+};
+
+// 或同步/异步接收
+using var pk = client.Receive();
+using var pk = await client.ReceiveAsync(cancellationToken);
+
+client.Close("完成"); // 或 client.Dispose()
+```
+
+### 3.3 请求-响应模式(需要管道编解码器)
+
+```csharp
+var client = new NetUri("tcp://127.0.0.1:8080").CreateRemote();
+client.Add<StandardCodec>();
+client.Open();
+
+var response = await client.SendMessageAsync(payload, cancellationToken); // 等待响应
+client.SendMessage(message); // 不等待响应
+```
+
+### 3.4 SSL/TLS
+
+```csharp
+// 服务端 SSL
+var server = new NetServer
+{
+ Port = 443,
+ SslProtocol = SslProtocols.Tls12,
+ Certificate = new X509Certificate2("server.pfx", "password"),
+};
+
+// 客户端 SSL(自动根据端口判断,或手动指定)
+var client = new NetUri("tcp://host:443").CreateRemote();
+if (client is TcpSession tcp)
+{
+ tcp.SslProtocol = SslProtocols.Tls12;
+ // tcp.Certificate = cert; // 客户端证书(如果服务端要求)
+}
+```
+
+---
+
+## 4. 管道与编解码器
+
+### 4.1 管道机制
+
+管道(`IPipeline`)是处理器链,Read/Write 返回值作为下一个处理器的输入,返回 `null` 截断管道:
+
+```
+接收:Socket → [Codec1.Read] → [Codec2.Read] → FireRead → OnReceive
+发送:SendMessage → [Codec2.Write] → [Codec1.Write] → FireWrite → Socket
+```
+
+Open 正序传播,Close 逆序传播。先添加的在底层(靠近 Socket),后添加的在上层(靠近业务)。
+
+### 4.2 内置编解码器
+
+| 编解码器 | 基类 | 说明 | 典型场景 |
+|---------|------|------|---------|
+| `StandardCodec` | `MessageCodec<IMessage>` | 4字节头部(Flag+Seq+Length),支持请求-响应匹配 | 自定义 RPC 协议 |
+| `LengthFieldCodec` | `MessageCodec<IPacket>` | 长度字段头部,可配置偏移和大小 | MQTT、通用二进制协议 |
+| `JsonCodec` | `Handler` | JSON 文本编解码,不处理粘包 | 文本协议(通常与 StandardCodec 级联) |
+| `SplitDataCodec` | `Handler` | 分隔符拆包(默认 `\r\n`) | 文本行协议 |
+| `WebSocketCodec` | `Handler` | WebSocket 帧编解码 | WebSocket 通信 |
+
+### 4.3 添加编解码器
+
+```csharp
+// 服务端添加
+server.Add<StandardCodec>();
+
+// 客户端添加
+client.Add<StandardCodec>();
+
+// 多层管道级联(按添加顺序组成链)
+server.Add<StandardCodec>(); // 底层:粘包拆包 + 请求响应匹配
+server.Add<JsonCodec>(); // 上层:JSON 编解码
+```
+
+### 4.4 StandardCodec 请求-响应
+
+StandardCodec 使用 `DefaultMessage`,包含 Flag(1字节)、Sequence(1字节)、Length(2字节),
+支持自动序列号分配和请求-响应匹配。
+
+```csharp
+// 服务端 Echo 示例
+server.Add<StandardCodec>();
+server.Received += (sender, e) =>
+{
+ if (sender is INetSession session && e.Message is IPacket pk)
+ session.SendReply(pk, e); // 使用 SendReply 关联请求上下文
+};
+
+// 客户端请求-响应
+client.Add<StandardCodec>();
+var response = await client.SendMessageAsync(payload);
+```
+
+### 4.5 基类选择
+
+| 基类 | 适用场景 | 典型代表 |
+|------|---------|---------|
+| `MessageCodec<T>` | 需要粘包拆包和/或请求-响应匹配(内置 `IMatchQueue`、`Encode`/`Decode`) | `StandardCodec`、`LengthFieldCodec` |
+| `Handler` | 简单转换、帧协议、文本协议(轻量,仅 `Read`/`Write`/`Open`/`Close`) | `JsonCodec`、`SplitDataCodec`、`WebSocketCodec` |
+
+### 4.6 编解码器设计规范
+
+#### 4.6.1 粘包拆包(PacketCodec 模式)
+
+TCP 是字节流协议,必须处理粘包拆包。统一模式(完整实现见 4.7 模板):
+
+1. 每个连接独立的 `PacketCodec` 实例,存储在 `ss["Codec"]` 中
+2. 通过 `GetLength2` 委托告诉 `PacketCodec` 如何计算完整帧长度
+3. `PacketCodec.Parse()` 返回完整帧列表,自动缓存不完整数据
+
+**`GetLength2` 规范**(签名 `Int32 GetLength(ReadOnlySpan<Byte> span)`):返回帧完整长度(含头部),数据不足时返回 `0`。
+
+```csharp
+public static Int32 GetLength(ReadOnlySpan<Byte> span)
+{
+ if (span.Length < 4) return 0;
+ var reader = new SpanReader(span) { IsLittleEndian = true };
+ reader.Advance(2);
+ return 4 + reader.ReadUInt16(); // 头部4字节 + 负载长度
+}
+```
+
+#### 4.6.2 编码与内存管理
+
+- **`ExpandHeader(size)`**:编码时优先复用负载缓冲区前置空间写入头部,零拷贝;空间不足时创建 `OwnerPacket`,原包作为 `Next` 链节点
+- **`SpanWriter`**:配合 `ExpandHeader` 写入头部字段,注意 `IsLittleEndian` 大小端
+- **兜底释放**:`MessageCodec<T>.Write` 基类自动 `TryDispose`;`Handler` 子类需在 `Write` 的 `finally` 中手动调用
+- **对象池**:`DefaultMessage.Rent()` / `DefaultMessage.Return()` 减少 GC 压力
+
+#### 4.6.3 请求-响应匹配
+
+`MessageCodec<T>` 内置 `IMatchQueue`,流程:`Write` → `AddToQueue` 入队 → `Decode` 解码 → `Queue.Match` 按 `IsMatch` 匹配 → 唤醒 `SendMessageAsync` 的 `Task`。
+
+- 重载 `AddToQueue`:控制哪些消息入队(通常只有请求消息)
+- 重载 `IsMatch`:根据序列号等字段匹配请求和响应(见 4.7 模板)
+- `QueueSize`:匹配队列大小,默认 256
+- `Timeout`:等待响应超时,默认 30_000ms
+- `UserPacket`:为 `true` 时向上层传递 `Payload` 而非整个 `IMessage`,用于编码器级联
+
+#### 4.6.4 Close 清理
+
+**必须**在 `Close` 中执行 `ss["Codec"] = null` 清理 `PacketCodec`,否则 `MemoryStream` 缓存泄漏(见 4.7 模板)。
+
+#### 4.6.5 上下文扩展(IExtend)
+
+管道处理器通过 `IExtend` 在会话/上下文上传递元数据:
+
+| 键 | 用途 | 示例 |
+|---|------|------|
+| `"Codec"` | 每连接的 `PacketCodec` 实例 | 编解码器的 `Decode`/`Close` 中读写 |
+| `"Flag"` | 数据类型标记 `DataKinds` | `JsonCodec.Write` 设置 → `StandardCodec.Write` 消费 |
+| `"_raw_message"` | 原始请求消息 | `MessageCodec.Read` 设置 → `Write` 中创建响应时消费 |
+| `"TaskSource"` | `TaskCompletionSource` | 框架内部,`AddToQueue` 消费 |
+
+#### 4.6.6 多层管道级联
+
+- 底层编解码器处理粘包拆包和请求-响应匹配,上层处理数据格式转换
+- `UserPacket = true` 让底层向上层传递 `Payload` 而非整个 `IMessage`
+- 上层通过 `ext["Flag"]` 向底层传递数据类型标记
+
+### 4.7 自定义编解码器模板
+
+#### 方式一:继承 MessageCodec<T>(需要粘包/请求响应匹配)
+
+```csharp
+/// <summary>自定义协议编解码器</summary>
+public class MyCodec : MessageCodec<MyMessage>
+{
+ /// <summary>编码消息为数据包</summary>
+ protected override Object? Encode(IHandlerContext context, MyMessage msg)
+ {
+ return msg.ToPacket();
+ }
+
+ /// <summary>解码数据包为消息</summary>
+ protected override IEnumerable<MyMessage>? Decode(IHandlerContext context, IPacket pk)
+ {
+ if (context.Owner is not IExtend ss) yield break;
+
+ if (ss["Codec"] is not PacketCodec pc)
+ {
+ ss["Codec"] = pc = new PacketCodec
+ {
+ GetLength2 = MyMessage.GetLength,
+ MaxCache = MaxCache,
+ Tracer = (context.Owner as ISocket)?.Tracer
+ };
+ }
+
+ foreach (var item in pc.Parse(pk))
+ {
+ var msg = new MyMessage();
+ if (msg.Read(item)) yield return msg;
+ }
+ }
+
+ /// <summary>是否匹配响应</summary>
+ protected override Boolean IsMatch(Object? request, Object? response) =>
+ request is MyMessage req && response is MyMessage res
+ && req.Sequence == res.Sequence;
+
+ /// <summary>连接关闭时清理</summary>
+ public override Boolean Close(IHandlerContext context, String reason)
+ {
+ if (context.Owner is IExtend ss) ss["Codec"] = null;
+
+ return base.Close(context, reason);
+ }
+}
+```
+
+#### 方式二:继承 Handler(简单转换/帧协议)
+
+```csharp
+/// <summary>自定义帧编解码器</summary>
+public class MyFrameCodec : Handler
+{
+ /// <summary>读取数据(接收时)</summary>
+ public override Object? Read(IHandlerContext context, Object message)
+ {
+ if (message is IPacket pk)
+ {
+ // 解码:二进制 → 业务对象
+ var frame = MyFrame.Parse(pk);
+ message = frame;
+ }
+
+ return base.Read(context, message);
+ }
+
+ /// <summary>写入数据(发送时)</summary>
+ public override Object? Write(IHandlerContext context, Object message)
+ {
+ IPacket? owner = null;
+ if (message is MyFrame frame)
+ {
+ // 编码:业务对象 → 二进制
+ message = owner = frame.ToPacket();
+ }
+
+ try
+ {
+ return base.Write(context, message);
+ }
+ finally
+ {
+ owner.TryDispose(); // 兜底释放
+ }
+ }
+
+ /// <summary>连接关闭时清理缓存</summary>
+ public override Boolean Close(IHandlerContext context, String reason)
+ {
+ if (context.Owner is IExtend ss) ss["Codec"] = null;
+
+ return base.Close(context, reason);
+ }
+}
+```
+
+---
+
+## 5. 常见模式与最佳实践
+
+### 5.1 端口选择
+
+- 测试代码使用端口 `0`(系统自动分配随机端口),避免端口冲突
+- 正式服务指定固定端口
+- 启动后可通过 `server.Port` 获取实际监听端口
+
+### 5.2 协议选择
+
+| 场景 | 推荐 |
+|------|------|
+| 可靠传输、长连接 | `NetType.Tcp` |
+| 低延迟、广播、允许丢包 | `NetType.Udp` |
+| 同时支持(默认) | `NetType.Unknown` |
+| Web 浏览器通信 | `NetType.WebSocket` |
+
+### 5.3 会话管理
+
+- `UseSession = true`(默认):维护会话集合,支持群发、按 ID 查找
+- `UseSession = false`:不维护会话集合,减少内存开销,适合海量短连接
+- `SessionTimeout`:设置会话超时时间(秒),超时无数据自动断开
+- 会话中通过 `Items` 字典存储自定义数据
+
+### 5.4 日志分层
+
+| 属性 | 用途 | 建议 |
+|------|------|------|
+| `Log` | 服务器应用层日志 | 始终设置 |
+| `SessionLog` | 会话级别日志 | 调试时设置 |
+| `SocketLog` | 底层 Socket 日志 | 仅 DEBUG 时设置 |
+| `LogSend` / `LogReceive` | 收发数据内容日志 | 仅 DEBUG 时开启 |
+| `Tracer` | 应用层 APM | 生产环境追踪 |
+| `SocketTracer` | Socket 层 APM | 排查底层问题 |
+
+### 5.5 资源释放
+
+- 服务端:调用 `server.Stop(reason)` 或 `server.Dispose()`
+- 客户端:调用 `client.Close(reason)` 或 `client.Dispose()`
+- 会话自动随连接断开释放,无需手动管理
+- `ISocketClient` 实现 `IDisposable`,推荐 `using` 模式
+
+### 5.6 INetHandler 业务处理器
+
+通过重载 `NetServer.CreateHandler` 注入自定义业务处理器:
+
+```csharp
+class MyServer : NetServer<MySession>
+{
+ /// <summary>为会话创建网络数据处理器</summary>
+ public override INetHandler? CreateHandler(INetSession session) => new MyHandler();
+}
+```
+
+处理器在会话 `Start` 时初始化,`OnReceive` 前调用 `Process`,适合前置协议解析。
+
+---
+
+## 6. 常见错误
+
+- ❌ 在 `OnReceive` 中执行长时间阻塞操作(会影响其他连接的数据接收)
+- ❌ 不加管道编解码器直接调用 `SendMessageAsync`(无法匹配响应)
+- ❌ 混淆 `Send` 与 `SendMessage`:前者直接发原始数据,后者经过管道编码
+- ❌ 混淆 `SendMessage` 与 `SendReply`:响应消息必须用 `SendReply` 关联请求上下文
+- ❌ 忘记调用 `base.OnConnected()` / `base.OnDisconnected(reason)` / `base.OnReceive(e)`
+- ❌ 在会话中使用 `Task.Result` 或 `Task.Wait()`(导致死锁和线程池饥饿)
+- ❌ 使用固定端口编写测试(端口冲突),应使用 `Port = 0`
+- ❌ 服务端 SSL 未指定证书
+
+---
+
+## 7. 完整示例
+
+### 7.1 带 StandardCodec 的 Echo 服务
+
+```csharp
+// 服务端
+var server = new NetServer
+{
+ Port = 8080,
+ ProtocolType = NetType.Tcp,
+ Log = XTrace.Log,
+};
+server.Add<StandardCodec>();
+server.Received += (sender, e) =>
+{
+ if (sender is INetSession session && e.Message is IPacket pk)
+ session.SendReply(pk, e);
+};
+server.Start();
+
+// 客户端
+var client = new NetUri($"tcp://127.0.0.1:{server.Port}").CreateRemote();
+client.Add<StandardCodec>();
+client.Open();
+
+var response = await client.SendMessageAsync(new ArrayPacket("Hello".GetBytes()));
+```
+
+### 7.2 自定义会话服务器
+
+```csharp
+class ChatServer : NetServer<ChatSession> { }
+
+class ChatSession : NetSession<ChatServer>
+{
+ protected override void OnConnected()
+ {
+ base.OnConnected();
+ Send($"欢迎 [{Remote}] 进入聊天室!\r\n");
+ }
+
+ protected override void OnReceive(ReceivedEventArgs e)
+ {
+ base.OnReceive(e);
+ var msg = e.Packet?.ToStr();
+ if (msg.IsNullOrEmpty()) return;
+
+ // 广播给所有在线用户
+ var host = (this as INetSession).Host;
+ host.SendAllMessage($"[{ID}] {msg}");
+ }
+
+ protected override void OnDisconnected(String reason)
+ {
+ base.OnDisconnected(reason);
+ WriteLog("用户离开:{0}", reason);
+ }
+}
+```
+
+---
+
+(完)
diff --git a/.github/instructions/xcode.instructions.md b/.github/instructions/xcode.instructions.md
index 2a79c8e..b1e1788 100644
--- a/.github/instructions/xcode.instructions.md
+++ b/.github/instructions/xcode.instructions.md
@@ -344,6 +344,44 @@ var count = User.FindCount(User._.Status == 1);
var maxId = User.FindMax(User._.Id, null);
```
+### 5.2.1 字段表达式方法参考
+
+`FieldItem`(`Entity._.FieldName`)提供丰富的表达式方法,可直接用于 `WhereExpression` 或 `FindAll` 条件:
+
+| 方法 | 说明 | 生成 SQL 示例 |
+|------|------|------|
+| `_.Name == value` | 等于 | `Name = 'test'` |
+| `_.Name != value` | 不等于 | `Name <> 'test'` |
+| `_.Id > value` | 大于 | `Id > 10` |
+| `_.Id >= value` | 大于等于 | `Id >= 10` |
+| `_.Id < value` | 小于 | `Id < 100` |
+| `_.Id <= value` | 小于等于 | `Id <= 100` |
+| `_.Name.Contains("x")` | 包含 | `Name Like '%x%'` |
+| `_.Name.NotContains("x")` | 不包含 | `Name Not Like '%x%'` |
+| `_.Name.StartsWith("x")` | 开头 | `Name Like 'x%'` |
+| `_.Name.EndsWith("x")` | 结尾 | `Name Like '%x'` |
+| `_.Id.In(list)` | In 操作 | `Id In(1,2,3)` |
+| `_.Id.NotIn(list)` | Not In | `Id Not In(1,2,3)` |
+| `_.Name.IsNull()` | 是否 NULL | `Name Is Null` |
+| `_.Name.NotIsNull()` | 不为 NULL | `Name Is Not Null` |
+| `_.Name.IsNullOrEmpty()` | NULL 或空串(仅 String) | `(Name Is Null Or Name='')` |
+| **`_.Name.NotIsNullOrEmpty()`** | **不为 NULL 且不为空串**(仅 String) | `(Name Is Not Null And Name<>'')` |
+| `_.Flag.IsTrue(true)` | 布尔真值 | `Flag=True` |
+| `_.Flag.IsTrue(false)` | 布尔假值/NULL | `Flag<>True Or Flag Is Null` |
+| `_.Id.Asc()` | 升序排序 | `Id Asc` |
+| `_.Id.Desc()` | 降序排序 | `Id Desc` |
+
+**空值判断常见写法**:
+```csharp
+// ✅ 推荐:使用内置方法
+exp &= _.DevName.NotIsNullOrEmpty(); // 字段不为空且不为空串
+
+// ❌ 不推荐:手写两个条件
+exp &= _.DevName != "" & _.DevName != null;
+```
+
+**注意**:`IsNullOrEmpty()` / `NotIsNullOrEmpty()` 仅支持 `String` 类型字段,非字符串字段请使用 `IsNull()` / `NotIsNull()`。
+
### 5.3 批量操作
```csharp
@@ -381,6 +419,82 @@ var list = User.FindAllWithCache();
var user = User.FindByKeyWithCache(1);
```
+### 5.6 Biz 文件数据层逻辑规范
+
+**核心理念**:所有需要人工编写的数据层逻辑代码,一律放在实体类的 Biz 文件(`*.Biz.cs`)中,包括**高级查询**(`#region 高级查询`)与**添删改查重载**等。外部调用方只传语义化参数,不感知 `WhereExpression` 拼接细节。
+
+#### 高级查询封装选择
+
+| 场景 | 方法形式 | 说明 | 示例 |
+|------|---------|------|------|
+| 返回单个对象,参数 ≤2 个 | `FindByXxx` | 未查到时返回 `null` | `FindByUserId(userId)` |
+| 返回列表,参数 ≤2 个,无模糊查询、无分页 | `FindAllByXxx` / `FindAllByXxxAndYyy` | 未查到时返回空列表,**不返回 null** | `FindAllByUserId(userId)` |
+| 参数较多,或含模糊查询,或含分页 | `Search(...)` | 未查到时返回空列表,**不返回 null** | `Search(userId, key, page)` |
+| 实体缓存内过滤(`Meta.Cache.FindAll(...)`) | `FindAllCachedXxx` / `FindCachedXxx` | — | `FindAllCachedEnabled()` / `FindCachedByQuestion(q)` |
+
+**命名约定说明**:
+- `FindByXxx`:返回**单个对象**(`TEntity?`),语义为"按条件查找一条记录",未找到返回 `null`
+- `FindAllByXxx`:返回**对象列表**(`IList<TEntity>`),语义为"按条件查找所有匹配记录",结果为空时返回空列表而非 `null`
+- `Search`:同样返回**对象列表**,结果为空时返回空列表而非 `null`
+
+#### Search 方法签名约定
+
+参数顺序(由左到右,按重要程度):
+
+```
+Search(业务过滤字段..., DateTime start, DateTime end, String? key, PageParameter page)
+```
+
+- 时间区间 `(DateTime start, DateTime end)` 放在 key / page 左边
+- 模糊查询关键词 `String? key` 放在 page 左边(倒数第二)
+- 分页参数 `PageParameter page` 始终最后
+
+#### 表达式简写
+
+在 Biz 文件的静态方法内部,可**省略类名前缀**:
+
+```csharp
+// ✅ 推荐(Biz 文件内部)
+var exp = _.UserId == userId;
+if (!keyword.IsNullOrEmpty()) exp &= _.Title.Contains(keyword.Trim());
+return FindAll(exp, page);
+
+// ❌ 避免(外部业务代码中拼接表达式)
+var exp = Conversation._.UserId == userId;
+if (!keyword.IsNullOrEmpty()) exp &= Conversation._.Title.Contains(keyword.Trim());
+var list = Conversation.FindAll(exp, p);
+```
+
+#### 示例
+
+```csharp
+// Biz 文件内 #region 高级查询
+
+/// <summary>根据用户编号查找最新一条会话</summary>
+/// <param name="userId">用户编号</param>
+/// <returns>会话对象,不存在时返回 null</returns>
+public static Conversation? FindByUserId(Int32 userId) => Find(_.UserId == userId);
+
+/// <summary>根据用户编号查找所有会话</summary>
+/// <param name="userId">用户编号</param>
+/// <returns>会话列表,不存在时返回空列表</returns>
+public static IList<Conversation> FindAllByUserId(Int32 userId) => FindAll(_.UserId == userId);
+
+/// <summary>分页搜索用户会话列表</summary>
+/// <param name="userId">用户编号</param>
+/// <param name="keyword">标题关键字,为空时不过滤</param>
+/// <param name="page">分页参数</param>
+/// <returns>会话列表,不存在时返回空列表</returns>
+public static IList<Conversation> Search(Int32 userId, String? keyword, PageParameter page)
+{
+ var exp = new WhereExpression();
+ exp &= _.UserId == userId;
+ if (!keyword.IsNullOrEmpty()) exp &= _.Title.Contains(keyword.Trim());
+
+ return FindAll(exp, page);
+}
+```
+
---
## 6. 运行时机制
diff --git a/NewLife.Yuque/NewLife.Yuque.csproj b/NewLife.Yuque/NewLife.Yuque.csproj
index 5000740..bbacba1 100644
--- a/NewLife.Yuque/NewLife.Yuque.csproj
+++ b/NewLife.Yuque/NewLife.Yuque.csproj
@@ -42,14 +42,14 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.103">
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.201">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
- <PackageReference Include="NewLife.Core" Version="11.13.2026.301" />
+ <PackageReference Include="NewLife.Core" Version="11.14.2026.402" />
</ItemGroup>
<ItemGroup>
diff --git a/NewLife.YuqueWeb/NewLife.YuqueWeb.csproj b/NewLife.YuqueWeb/NewLife.YuqueWeb.csproj
index 4e85bf7..37378c2 100644
--- a/NewLife.YuqueWeb/NewLife.YuqueWeb.csproj
+++ b/NewLife.YuqueWeb/NewLife.YuqueWeb.csproj
@@ -39,13 +39,13 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.103">
+ <PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.201">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="NewLife.Core" Version="11.13.2026.301" />
- <PackageReference Include="NewLife.Cube.Core" Version="6.9.2026.303" />
- <PackageReference Include="NewLife.XCode" Version="11.25.2026.302" />
+ <PackageReference Include="NewLife.Core" Version="11.14.2026.402" />
+ <PackageReference Include="NewLife.Cube.Core" Version="6.10.2026.404" />
+ <PackageReference Include="NewLife.XCode" Version="11.25.2026.403" />
</ItemGroup>
<ItemGroup>
diff --git a/XUnitTest/XUnitTest.csproj b/XUnitTest/XUnitTest.csproj
index dcb385c..c0bae63 100644
--- a/XUnitTest/XUnitTest.csproj
+++ b/XUnitTest/XUnitTest.csproj
@@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.3" />
+ <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
diff --git a/YqWeb/YqWeb.csproj b/YqWeb/YqWeb.csproj
index d515d16..906354e 100644
--- a/YqWeb/YqWeb.csproj
+++ b/YqWeb/YqWeb.csproj
@@ -19,7 +19,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="NewLife.Stardust.Extensions" Version="3.7.2026.303" />
+ <PackageReference Include="NewLife.Stardust.Extensions" Version="3.7.2026.403" />
</ItemGroup>
<ItemGroup>