NewLife/NewLife.Remoting

docs: 同步 v12 兼容性改造登记与二次评估

- 登记 V12-1~V12-16 兼容性改造与相对 3.9 的行为变化、Core 反馈项
- 新增二次评估(对齐 12.0.2026.1009-beta0916)V12-17~V12-23,更新 V12-15 与 CORE-2/CORE-4 处置状态

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
大石头 authored at 2026-10-09 20:00:46
0354c16
Tree
1 Parent(s) 0ab58f3
Summary: 1 changed files with 73 additions and 0 deletions.
Modified +73 -0
Modified +73 -0
diff --git "a/Doc/\345\212\237\350\203\275\346\270\205\345\215\225.md" "b/Doc/\345\212\237\350\203\275\346\270\205\345\215\225.md"
index 8758fa4..19bd7de 100644
--- "a/Doc/\345\212\237\350\203\275\346\270\205\345\215\225.md"
+++ "b/Doc/\345\212\237\350\203\275\346\270\205\345\215\225.md"
@@ -230,3 +230,76 @@
 | KNOWN-1 | RPC | `WsClient` 请求/响应为单帧收发,不支持多路复用;`InvokeWithClient` 同步等待 | 记录为已知限制,本次不重构 |
 | KNOWN-2 | SRV | `ApiFilterAttribute` ModelState 校验被注释禁用,启用可能影响既有调用方 | 保持现状,文档说明 |
 | KNOWN-3 | CLNT | `ClientBase` 单文件约1500行,职责较重 | 记录,不拆分(重构风险高) |
+
+---
+
+## v12 兼容性改造(2026-10)
+
+> 起因:NewLife.Core 升级到 12.x 测试版(`MessagePump` + `IMessageCodec` 新帧协议模型)后,Remoting 的封包协议、HTTP/WebSocket 编解码全面失效。下表为逐项排查后的登记,其中「状态」列:✅ 已修复/⚠️ 已知限制/⏳ 后续独立任务。
+>
+> 本轮为**兼容性适配评估,不是正式发版**:`ChangeLog.md`、`VersionPrefix`、`PackageReleaseNotes` 均保持 3.9 原状,待正式发版时再统一更新。破坏性变更见文末「相对 3.9 的行为变化」。
+>
+> 2026-10-09 二次评估:Core 引用升级到 `12.0.2026.1009-beta0916`(含 10-08/10-09 的异步处理器契约、UDP 统一匹配等改动),重评估结论见「二次评估」小节。
+
+| 编号 | 模块 | 大白话说的问题 | 状态 |
+|:----:|:----:|----------------|:----:|
+| V12-1 | 编解码 | Core 把「封包协议」从管道处理器换成了编解码器,旧的 `StandardCodec` 没了,服务端/客户端的协议挂载方式全部要改 | ✅ |
+| V12-2 | HTTP | 自研 HTTP 帧栈(`HttpCodec`)按新接口重写代价高且与 Core 重复,改为**直接用 NewLife.Core 的 `HttpServer`** 承载 HTTP 入口,Remoting 只保留动作分发桥接 | ✅ |
+| V12-3 | WS | SRMP-over-WS 通道取消:服务端不必再写「HTTP/WS 双模式编解码器」与「WS 承载 SRMP 复合编解码器」,SRMP 只走 tcp/udp | ✅ |
+| V12-4 | WS | ws:// 客户端不再可用:`ApiClient` 收到 ws 地址即抛 `NotSupportedException`;服务端对 WS 握手请求明确回 501,不让客户端拿到 101 后空等 | ✅ |
+| V12-5 | SRV | v12 一台服务器不能再用同一个端口同时跑 tcp/udp/ws/http,改为**拆端口**:SRMP 用 `Port`,HTTP 用 `HttpPort`(默认 **-1 不启用**;设为 0 取 `Port+1`,被占用时回退随机端口) | ✅ |
+| V12-6 | SRV | HTTP 会话编码器与主机主编码器互相覆盖,所有 RPC 请求超时;改用会话级编码器(`session["Encoder"]`)后,启动后「恢复主编码器」的补丁也随之取消 | ✅ |
+| V12-7 | 大帧 | v12 帧泵「头部到齐就交付、消息体边到边给」,超过 8k 的大帧原来会被当成残缺帧卡住甚至超时;现在由会话层/客户端接收链读满整帧后再处理 | ✅ |
+| V12-8 | UDP | v12 的 UDP 不再支持「发请求-等响应」,Remoting 自己维护序列号配对表来等响应;未配置超时(`Timeout<=0`)时不再建即时到期的延迟任务,避免响应未到就误判超时 | ✅ |
+| V12-9 | 并发 | 并发请求的序列号恒为 0,响应会串台;现在由客户端分配递增序列号 | ✅ |
+| V12-10 | 客户端 | 请求-响应匹配等待时间(`SessionBase.MatchTimeout`)默认 30 秒,会把更长的 `ApiClient.Timeout` 配置截断;现随 `Timeout` 同步设置,两个超时口径一致 | ✅ |
+| V12-11 | 兼容 | `IEncoder` 里 3 处过时的 `Slice(..., true)` 调用改为新 API,消除 30 条编译警告 | ✅ |
+| V12-12 | SRV | `ApiServer.Stop` 之后重新 `Start`,HTTP 端口会静默失效(旧实例没被重新拉起);现在会重启已创建的 HTTP 服务器 | ✅ |
+| V12-13 | 测试 | 回归测试按新架构调整:删除 ws 帧层用例,HTTP 用例显式开启 `HttpPort`,新增「WebSocket 握手被拒绝」「Stop 后重启 HTTP 仍可用」「HTTP 入口默认不启用」;全套单元与集成测试 659 通过 / 0 失败 / 2 跳过 | ✅ |
+| V12-14 | UDP | UDP 单个数据报受 Core `Setting.BufferSize`(默认 8k)限制,超过会被截断丢弃(v11 同样如此,非本次引入) | ⚠️ |
+| V12-15 | 环境 | `NetServer` 在 `Port=0` 时会把第一个随机端口强塞给全部 4 个 socket,若该端口落在别的协议保留区间会 `WSAEACCES`;另外测试类都开了 `ReuseAddress`(SO_REUSEADDR),并发跑整套测试时两个 ApiServer 可能绑到同一端口。实测:5 次全量运行中 1 次出现「单测试类 22 项全部失败」,该类单独运行 29/29 通过 | ✅ |
+| V12-16 | HTTP | HTTP 入口不支持服务端流式推送(`IAsyncEnumerable` 动作):HTTP 是一次请求-响应,会话壳在会话推送时抛 `NotSupportedException`,客户端收到 500;流式动作请走 tcp/udp | ⚠️ |
+
+### 二次评估(对齐 12.0.2026.1009-beta0916)
+
+> Core 于 10-08/10-09 又有一批改动(`IHttpHandler` 契约异步化、消息泵下沉 `TcpSession`、UDP 接入统一响应匹配、`WebSocketClient` 自动回帧等)。本节为升级到 `12.0.2026.1009-beta0916` 后的重评估结果;上一轮已修复的 V12-1~V12-16 逐项复核结论不变,未列项即为无影响。
+
+| 编号 | 模块 | 大白话说的问题 | 状态 |
+|:----:|:----:|----------------|:----:|
+| V12-17 | HTTP | Core 把 `IHttpHandler` 处理契约整体异步化(`ProcessRequest` 变为 `Task ProcessRequestAsync`),`ApiHttpHandler` 不再满足接口而编译失败;改为异步实现并删除同步桥 | ✅ |
+| V12-18 | UDP | Core 为 UDP 接入统一响应匹配(`UdpServer`/`UdpSession.SendMessageAsync` + `ResponseMatcher`),V12-8 的自建配对(`_waits`/`WaitDatagramAsync`/`TryMatchWait` 约 100 行)删除,TCP/UDP 统一走核心库匹配(即 CORE-2 反馈的落地) | ✅ |
+| V12-19 | UDP | 会话级 `MatchTimeout` 不从 `UdpServer` 继承(Core 只下发协议与并发度),经 `ApiUdpServer` 子类在新建会话时补齐;`Timeout<=0` 用 `Int32.MaxValue` 保持「不限时」,不被默认 30 秒截断 | ✅ |
+| V12-20 | WS | Core 的 `WebSocketClient` 已自动回 Pong/Close,`WsChannel` 里重复的手动 Pong 删除(应用层文本心跳保留) | ✅ |
+| V12-21 | 测试 | 对齐 Core「显式端口」约定,新增 `XUnitTest/TestPort.cs`(`CheckPort` 预检 + TCP/UDP 双栈探测、进程内不复用),替换测试集 73 处 `new ApiServer(0)`;动态端口用例改为单协议 TCP 验证,多实例用例改验证端口互不冲突(V12-15 收官) | ✅ |
+| V12-22 | 测试 | 测试代码清掉 Core 过时 API 告警(`SpanReader.FreeCapacity` → `Capacity - Position`) | ✅ |
+| V12-23 | 样例 | 登录令牌改 JWT 后超过样例在线表 `Token` 列的 200 字符上限,`Node/Ping` 保存失败;ZeroServer/IoTZero 两处 Token 列加宽到 500(对齐上游 Stardust),设备历史 `Remark` 补按列长截断。样例集成测试由 11/18 恢复到 **18/18** | ✅ |
+
+### 相对 3.9 的行为变化(正式发版时需写入 ChangeLog)
+
+**破坏性变更**
+
+| 变更 | 影响与替代方案 |
+|------|----------------|
+| `WsClient` 类删除 | ws:// 承载 SRMP 的能力取消。SRMP 请用 `ApiClient` 的 `tcp://`/`udp://`;HTTP 调用请用 `ApiHttpClient`(`http://`/`https://`) |
+| `Http.HttpCodec`、`WebSocketClientCodec`、`WebSocketServerCodec` 删除 | 自研 HTTP/WS 帧栈整体退场,HTTP 入口改用 NewLife.Core 的 `HttpServer`;自定义帧协议请直接实现 `IMessageCodec` |
+| `HttpMessage` 帧层方法删除(`TryParse`/`WriteHeader`/`Read`/`ToPacket` 等) | 该类只承载动作分发所需的头部与负载;HTTP 报文的解析与写出由 Core 负责 |
+| 同端口三协议取消 | `ApiServer` 主端口只跑 SRMP;HTTP 需显式设置 `HttpPort`(默认关闭)。原「一个端口既跑 RPC 又跑 HTTP/WS」的部署需改为双端口 |
+| `IApiHost.GetMessageCodec()` 返回类型 | 由 `IPipelineHandler` 改为 `IMessageCodec`(重载该方法的子类需同步调整,返回 `SrmpCodec` 即保持原协议) |
+| `RemotingServiceResolver` 不再解析 ws/wss 地址 | 配置里的 `ws://`、`wss://` 地址会抛 `NotSupportedException`,请改为 `tcp://`/`udp://` 或 `http(s)://` |
+
+**行为变化(兼容)**
+
+- HTTP 入口基于 Core `HttpServer`:keep-alive、分块传输、`Expect: 100-continue`、HEAD 等 HTTP/1.1 语义由 Core 处理;路由为兜底 `/{*path}`,动作名仍按 URI 解析(`/Api/Info` → `Api/Info`),参数与令牌传递方式不变。
+- WS 握手请求收到 `501 Not Implemented`(明确不支持),而不是被静默升级后空等。
+- 大帧(超过 Socket 缓冲区)与并发请求在 v12 下的行为与 3.9 一致:由 SDK 内部完成流式体物化与序列号配对。
+
+### 需向 NewLife.Core 反馈的点
+
+| 编号 | 描述 | 影响 |
+|:----:|------|------|
+| CORE-1 | `Setting.BufferSize` 默认 8k 同时用于 TCP 接收缓冲与 UDP 数据报缓冲,UDP 发送超过 8k 的数据报会被静默截断(`UdpServer` 仅在 `MessageSize` 错误后自增缓冲,当前数据报已丢失) | UDP 大包不可用,且无显式报错 |
+| CORE-2 | `UdpSession.SendMessageAsync` 在协议模式下抛 `NotSupportedException`,UDP 请求-响应需应用层自建配对 | 每个上层框架都要自己实现一遍。**已在 12.0.2026.1009 落地**:UDP 接入统一响应匹配,Remoting 自建配对已删除(V12-18) |
+| CORE-3 | 帧泵「头部到齐即交付、体流式」语义下,消费方若忘记 `ReadAllAsync` 物化整帧,会拿到不完整负载(Core 的 `WebSocketClient` 内部已自行物化,但对外文档未强调) | 迁移期易踩坑 |
+| CORE-4 | `NetServer` 动态端口(`Port=0`)时把第一个随机端口复用给 tcp/udp/ws/http 四个 socket,可能落入系统保留区间导致绑定失败;若调用方开了 `ReuseAddress`,两个实例还可能绑到同一端口,表现为「服务端已启动但连接落到别的实例」 | 单元测试/服务启动偶发失败。**12.0.2026.1009 已决**:Core 取消随机端口重试(启动失败直接抛出),按「测试与部署一律显式指定端口」处置,Remoting 测试侧已加固(V12-21) |
+| CORE-5 | `HttpSession` 对每个请求先于路由尝试 WebSocket 握手,框架无法按路由或开关禁用 WS;拒绝升级只能由处理器覆盖 101 响应,且连接仍被留在 WS 模式(不会自动关闭) | 只想提供纯 HTTP 的服务器无法干净地拒绝 WS 升级 |
+