chore: 准备 v4.0.2026.1024 发版 - 大版本对齐 NewLife.Core v12:主端口仅跑 SRMP、HTTP 独立端口,移除 ws:// 承载 SRMP - 升级 NewLife.Core 至 12.0.2026.1024,Redis/IoT/MQTT 至最新 beta - 补充 v4.0 ChangeLog、版本号、PackageReleaseNotes 与破坏性变更说明 - 发布工作流对齐 NewLife.Core:Ubuntu 打包,GitHub Packages + NuGet.org + GitHub Release - README 增加 v4 升级说明并同步 11 个语种 - 31 种语言词表全量覆盖,词表门禁拦截任何回退英文的词条大石头 authored at 2026-10-24 11:57:56 大石头 committed at 2026-10-24 12:18:55
diff --git a/.github/workflows/publish-beta.yml b/.github/workflows/publish-beta.yml
index c9f7d8d..e617f35 100644
--- a/.github/workflows/publish-beta.yml
+++ b/.github/workflows/publish-beta.yml
@@ -13,8 +13,14 @@ on:
- 'NewLife.Remoting.Extensions/**'
workflow_dispatch:
+# GitHub Packages 推送需要 packages: write
+permissions:
+ contents: read
+ packages: write
+
jobs:
build-publish:
+ # 打包不依赖 Windows 目标包:net45/net461 由 SDK 自动引入 ReferenceAssemblies(已在 Linux 实测全 TFM 打包)
runs-on: ubuntu-latest
steps:
@@ -23,11 +29,10 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
- 6.x
- 7.x
8.x
- 9.x
10.x
+ cache: true
+ cache-dependency-path: '**/*.csproj'
- name: Get Version
run: echo "VERSION=$(date '+%Y.%m%d-beta%H%M')" >> $GITHUB_ENV
- name: Build
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index dee8179..fe451ea 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -5,8 +5,14 @@ on:
tags: [ v* ]
workflow_dispatch:
+# 必须同时声明:缺 packages: write 会让 GitHub Packages 推送 403,缺 contents: write 无法创建 Release
+permissions:
+ contents: write
+ packages: write
+
jobs:
build-publish:
+ # 打包不依赖 Windows 目标包:net45/net461 由 SDK 自动引入 ReferenceAssemblies(已在 Linux 实测全 TFM 打包)
runs-on: ubuntu-latest
steps:
@@ -15,11 +21,10 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
- 6.x
- 7.x
8.x
- 9.x
10.x
+ cache: true
+ cache-dependency-path: '**/*.csproj'
- name: Get Version
run: echo "VERSION=$(date '+%Y.%m%d')" >> $GITHUB_ENV
- name: Build
@@ -27,7 +32,84 @@ jobs:
dotnet pack --version-suffix ${{ env.VERSION }} -c Release -o out NewLife.Remoting/NewLife.Remoting.csproj
dotnet pack --version-suffix ${{ env.VERSION }} -c Release -o out NewLife.Remoting.Extensions/NewLife.Remoting.Extensions.csproj
- - name: Publish
+ - name: Publish to GitHub Packages
+ id: ghpackages
+ # 备用镜像渠道,失败只告警,不阻断 NuGet.org 正式发布
+ continue-on-error: true
run: |
dotnet nuget push ./out/*.nupkg --skip-duplicate --source https://nuget.pkg.github.com/NewLifeX/index.json --api-key ${{ github.token }}
+ - name: Publish to NuGet.org
+ id: nuget
+ run: |
dotnet nuget push ./out/*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.nugetKey }}
+ - name: Create GitHub Release
+ id: release
+ # 仅 tag 推送时创建;手工触发(master 无 tag)只发包
+ if: startsWith(github.ref, 'refs/tags/')
+ # 观察期:失败只告警,不影响已完成的发包;结尾 Publish Summary 会显式标红
+ continue-on-error: true
+ shell: pwsh
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ $ErrorActionPreference = 'Stop'
+ $tag = $env:GITHUB_REF_NAME
+ $repo = $env:GITHUB_REPOSITORY
+
+ # 幂等:同 tag 已有 Release 就直接跳过,失败重跑不会报错
+ gh release view $tag --repo $repo *> $null
+ if ($LASTEXITCODE -eq 0) {
+ Write-Host "Release $tag 已存在,跳过"
+ exit 0
+ }
+
+ # 发布说明优先取 ChangeLog.md 中当前版本段落(标题形如 "## v4.0.2026.1024 (2026-10-24)")
+ $version = $tag.TrimStart('v')
+ $pattern = '^##\s+v?' + [regex]::Escape($version) + '(\s|\(|$)'
+ $lines = Get-Content -LiteralPath 'ChangeLog.md'
+ $start = -1
+ for ($i = 0; $i -lt $lines.Count; $i++) {
+ if ($lines[$i] -match $pattern) { $start = $i; break }
+ }
+
+ $ghArgs = @('release', 'create', $tag, '--repo', $repo, '--title', $tag, '--verify-tag')
+ if ($start -ge 0) {
+ $end = $lines.Count
+ for ($i = $start + 1; $i -lt $lines.Count; $i++) {
+ if ($lines[$i] -match '^##\s') { $end = $i; break }
+ }
+ $notes = Join-Path $env:RUNNER_TEMP 'release-notes.md'
+ $lines[$start..($end - 1)] | Set-Content -LiteralPath $notes -Encoding utf8
+ $ghArgs += @('--notes-file', $notes)
+ Write-Host "发布说明取自 ChangeLog.md 的 $version 段落($($end - $start) 行)"
+ }
+ else {
+ $ghArgs += '--generate-notes'
+ Write-Host "ChangeLog.md 未找到 $version 段落,改用自动生成说明"
+ }
+
+ $assets = @(Get-ChildItem -LiteralPath 'out' -File | Where-Object { $_.Extension -in '.nupkg', '.snupkg' } | ForEach-Object { $_.FullName })
+ $ghArgs += $assets
+
+ & gh @ghArgs
+ if ($LASTEXITCODE -ne 0) { throw "gh release create 失败,退出码 $LASTEXITCODE" }
+
+ Write-Host "已创建 Release $tag,附加资产 $($assets.Count) 个"
+ - name: Publish Summary
+ if: always()
+ shell: pwsh
+ run: |
+ $rows = [ordered]@{
+ 'GitHub Packages' = '${{ steps.ghpackages.outcome }}'
+ 'NuGet.org' = '${{ steps.nuget.outcome }}'
+ 'GitHub Release' = '${{ steps.release.outcome }}'
+ }
+ $marks = @{ success = '✅'; failure = '⚠️'; skipped = '⏭'; cancelled = '⛔' }
+ $lines = @('### 发布结果', '', '| 渠道 | 结果 |', '| --- | --- |')
+ foreach ($k in $rows.Keys) {
+ $o = $rows[$k]
+ $lines += "| $k | $($marks[$o]) $o |"
+ }
+ $lines += ''
+ $lines += '> GitHub Release 仅 tag 推送时创建;手工触发只发包。'
+ $lines | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Encoding utf8 -Append
diff --git a/Benchmark/NewLife.Remoting.Benchmarks.csproj b/Benchmark/NewLife.Remoting.Benchmarks.csproj
index ae36b02..2f4d53d 100644
--- a/Benchmark/NewLife.Remoting.Benchmarks.csproj
+++ b/Benchmark/NewLife.Remoting.Benchmarks.csproj
@@ -13,7 +13,7 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
- <PackageReference Include="NewLife.Core" Version="12.0.2026.1021-beta0321" />
+ <PackageReference Include="NewLife.Core" Version="12.0.2026.1024" />
</ItemGroup>
<ItemGroup>
diff --git a/ChangeLog.md b/ChangeLog.md
index f62d41e..c68b7c7 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,59 @@
# 新生命远程通信库(NewLife.Remoting)变更日志
+## v4.0.2026.1024 (2026-10-24)
+
+> 本次为对齐 NewLife.Core v12 网络层重构的大版本升级,包含端口模型、协议入口与运行时消息键的破坏性变更,升级前请阅读下方「破坏性变更」。
+
+### NativeAOT 与源生成器
+- **NativeAOT 支持**:net8.0+ 常驻 AOT 兼容分析,库自身告警清零;新增动作静态表与流式泵,AOT 下零反射调用
+- **内置源生成器**:新增 NewLife.Remoting.SourceGenerator,扫描 `Register<T>()` 调用点生成强类型委托、流式泵与签名类型保留,随主包 analyzers 分发,未命中自动回退反射
+- **单方法注册**:源生成器支持 `Register(controller, "Method")` 实例入口,并保留按名查找所需的可反射元数据
+- **AOT 冒烟矩阵**:新增 AotSmoke、AotSmoke.Web、AotSmoke.XCode 三个冒烟工程,覆盖核心链路、最小 API 宿主与 XCode 设备链路
+
+### NewLife.Core v12 适配
+- **消息泵与 HttpServer**:SRMP 封包改由 Core 消息泵编解码器承载,HTTP 入口改用 Core HttpServer,SRMP 与 HTTP 端口拆分,`HttpPort` 默认关闭
+- **UDP 请求响应匹配**:改用 Core 内置 ResponseMatcher,删除自建配对表
+- **移除 SRMP-over-WS**:删除自研 HTTP/WS 帧栈,`ws://` 地址明确抛 `NotSupportedException`,WebSocket 握手返回 501
+
+### 安全增强
+- **令牌吊销**:`TokenService.RevokeToken` 支持令牌黑名单,设备注销自动吊销当前访问令牌;访问令牌与刷新令牌分离,旧令牌平滑兼容
+- **可信代理**:`GetUserHost` 支持单一 IP 折叠,并按 `TrustedProxies` 解析 `X-Forwarded-For`,防止客户端伪造来源地址
+
+### 最小 API 端点
+- **MapRemoting**:Extensions 新增最小 API 端点宿主,登录、注销、心跳、升级、WebSocket、SSE 通知、命令回执与事件上报端点与 MVC 控制器镜像实现同一协议
+- **AddRemotingCore**:拆出不含 MVC 配置的注册入口,供 NativeAOT 宿主使用
+
+### 多语言本地化
+- **31 种语言**:核心库与扩展库词表扩展至 31 种语言,语言集合与 NewLife.Core 对齐;词表门禁强制逐条覆盖,仅 `MD5` 等专有名词保留原文
+- **请求语言协商**:支持按 `Accept-Language` 选择请求语言,宿主启用 `RequestLocalization` 时优先让位宿主
+- **元数据与门禁**:程序集标题与描述英文化并纳入词表;词表门禁拦截未翻译的句子级词条
+
+### 测试与质量
+- **测试覆盖**:补充 AOT、最小 API、令牌吊销、可信代理、请求语言与本地化回归测试,测试端口改为跨进程独占分片
+- **CI 矩阵**:新增 Android/iOS 移动端与 riscv64/s390x 指令集冒烟,重写多 RID 测试矩阵并纳入龙芯 LA64 观察期
+- **示例修复**:Samples 夹具预热服务端并按时间预算重试登录,修复 IoTZero 预置产品与实体缓存的竞争
+
+### 依赖更新
+- **NewLife 组件**:升级 NewLife.Core 至 12.0.2026.1024,XCode、Redis、IoT、MQTT、Stardust 至最新 beta
+
+### 破坏性变更
+- **`WsClient` 删除**:`ws://` 承载 SRMP 的能力取消;SRMP 请改用 `ApiClient` 的 `tcp://`/`udp://`,HTTP 调用请改用 `ApiHttpClient` 的 `http(s)://`
+- **自研 HTTP/WS 帧栈删除**:`Http.HttpCodec`、`WebSocketClientCodec`、`WebSocketServerCodec` 移除,HTTP 入口改由 NewLife.Core `HttpServer` 承载;自定义帧协议请实现 `IMessageCodec`
+- **`HttpMessage` 帧层方法删除**:`TryParse`/`WriteHeader`/`Read`/`ToPacket` 等移除,该类仅保留动作分发所需的头部与负载
+- **同端口三协议取消**:`ApiServer` 主端口只跑 SRMP,HTTP 需显式设置 `HttpPort`(默认关闭);原单端口同时承载 RPC/HTTP/WS 的部署需改为双端口
+- **`IApiHost.GetMessageCodec()` 返回类型变更**:由 `IPipelineHandler` 改为 `IMessageCodec`,重载该方法的子类需同步调整(返回 `SrmpCodec` 即保持原协议)
+- **`RemotingServiceResolver` 不再解析 ws/wss**:配置中的 `ws://`/`wss://` 地址会抛 `NotSupportedException`,请改为 `tcp://`/`udp://` 或 `http(s)://`
+- **目标框架由 10 个收敛为 7 个**:不再直接提供 net5.0/net7.0/net9.0 资产(net5.0→netstandard2.1、net7.0→net6.0、net9.0→net8.0),NuGet 自动回退,消费方无需改框架
+- **运行时消息改为英文原文**:异常与日志文案以英文原文作为词表键;按中文文案做字符串匹配的调用方需改用 `ApiCode` 或错误码判断
+
+### Bug 修复
+- **[fix]** 修复简单类型返回值未按目标类型解码导致调用失败
+- **[fix]** 修复请求取消时底层空响应被误判为“服务端返回空”,改为抛出超时异常
+- **[fix]** 修复带前缀日志包装器导致本地化失效
+- **[fix]** 修正令牌密钥格式提示中的词内空格
+
+---
+
## v3.9.2026.0901 (2026-09-01)
### 客户端健壮性
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 3583773..7d73366 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"
@@ -216,9 +216,9 @@
---
-## 发版待办:相对 3.9 的行为变化(正式发版时需写入 ChangeLog)
+## 升级对照:相对 3.9 的行为变化(已随 v4.0.2026.1024 写入 ChangeLog)
-> v12 适配已完成但尚未正式发版:`ChangeLog.md`、`VersionPrefix`、`PackageReleaseNotes` 保持 3.9 原状,正式发版时统一更新,并把下述行为变化写入 ChangeLog。
+> v12 适配已随 v4.0.2026.1024 正式发版,以下行为变化已写入 `ChangeLog.md`;本节保留作为升级对照,不再作为发版待办。
**破坏性变更**
@@ -255,7 +255,7 @@
| 项 | 位置 | 处置与理由 |
|---|---|---|
-| `MD5: {0}`、`Encoder: {0}`、`Handler: {0}` 在部分语言保留英文,`Failover` 在部分语言沿用借词 | `Locales/*.yml` | 目标语言的 IT 惯用写法即原文(德语/荷兰语等以 `Failover` 为标准词),属约定译法;`check-locales.ps1` 只对句子级词条做未翻译拦截 |
+| `MD5: {0}` 在全部语言保留英文 | `Locales/*.yml` | `MD5` 是哈希算法专有名词,不可翻译;`check-locales.ps1` 将其列入未翻译白名单,其余词条(含 `Encoder`/`Handler`/`Failover`)一律要求本族语译文 |
| 表结构元数据(XCode 模型字段描述)保持母语 | 各 `Model.xml` | 表结构元数据不是运行时消息,翻译会让写入库的历史数据前后不一致 |
| 设备历史记录标签与明细保持中文 | `DefaultDeviceService.WriteHistory` | 按持久化数据对待,展示层可按标签码翻译 |
| `PackageReleaseNotes` 仍为中文 | 各 csproj | 发版流程专属字段,日常提交不改动,发版时统一整理 |
diff --git a/NewLife.Remoting.Extensions/NewLife.Remoting.Extensions.csproj b/NewLife.Remoting.Extensions/NewLife.Remoting.Extensions.csproj
index 829af11..6cb027f 100644
--- a/NewLife.Remoting.Extensions/NewLife.Remoting.Extensions.csproj
+++ b/NewLife.Remoting.Extensions/NewLife.Remoting.Extensions.csproj
@@ -9,7 +9,7 @@
<Description>Device access controllers, token services and session management extensions for NewLife.Remoting.</Description>
<Company>新生命开发团队</Company>
<Copyright>©2002-2026 NewLife</Copyright>
- <VersionPrefix>3.9</VersionPrefix>
+ <VersionPrefix>4.0</VersionPrefix>
<VersionSuffix>$([System.DateTime]::Now.ToString(`yyyy.MMdd`))</VersionSuffix>
<Version>$(VersionPrefix).$(VersionSuffix)</Version>
<FileVersion>$(Version)</FileVersion>
@@ -39,9 +39,9 @@
<PackageIcon>leaf.png</PackageIcon>
<RepositoryUrl>https://github.com/NewLifeX/NewLife.Remoting</RepositoryUrl>
<RepositoryType>git</RepositoryType>
- <PackageTags>新生命团队;X组件;NewLife;$(AssemblyName)</PackageTags>
+ <PackageTags>新生命团队;X组件;NewLife;RPC;HTTP;IoT;NativeAOT;AOT;$(AssemblyName)</PackageTags>
<PackageDescription>Device access controllers, token services and session management extensions for NewLife.Remoting. 提供设备接入控制器、令牌服务与会话管理扩展。</PackageDescription>
- <PackageReleaseNotes>登录失败自动重试,配合滚动登录超时设计,服务端冷启动时自动恢复;ZeroServer/IoTZero 示例客户端改用 Open 规范用法,响应总线自测恢复执行;消除编译警告;升级 NewLife.Core 依赖</PackageReleaseNotes>
+ <PackageReleaseNotes>v4 大版本:对齐 NewLife.Core v12 网络层,主端口仅跑 SRMP、HTTP 需显式 HttpPort,移除 ws:// 承载 SRMP 与自研 HTTP/WS 帧栈;支持 NativeAOT 并内置源生成器;新增令牌吊销与访问/刷新令牌分离,GetUserHost 支持可信代理防伪造;新增最小 API 端点宿主,与 MVC 控制器镜像实现同一协议;运行时词表扩展至 31 种语言;升级 NewLife.Core 12.0.2026.1024</PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
@@ -72,7 +72,7 @@
</None>
</ItemGroup>
<ItemGroup>
- <PackageReference Include="NewLife.Core" Version="12.0.2026.1021-beta0321" />
+ <PackageReference Include="NewLife.Core" Version="12.0.2026.1024" />
<PackageReference Include="NewLife.XCode" Version="12.2.2026.1021-beta0423" />
</ItemGroup>
<ItemGroup>
diff --git a/NewLife.Remoting.SourceGenerator/NewLife.Remoting.SourceGenerator.csproj b/NewLife.Remoting.SourceGenerator/NewLife.Remoting.SourceGenerator.csproj
index 5547b00..ba27f63 100644
--- a/NewLife.Remoting.SourceGenerator/NewLife.Remoting.SourceGenerator.csproj
+++ b/NewLife.Remoting.SourceGenerator/NewLife.Remoting.SourceGenerator.csproj
@@ -6,7 +6,7 @@
<Description>Source generator of NewLife.Remoting for NativeAOT-friendly compile-time code generation. NewLife.Remoting 源生成器:随主包分发,为 ApiServer.Register<T>() 注册的控制器生成静态动作调用委托与流式泵(生成物为纯静态代码,天然 AOT 安全)。</Description>
<Company>新生命开发团队</Company>
<Copyright>©2002-2026 NewLife</Copyright>
- <VersionPrefix>3.9</VersionPrefix>
+ <VersionPrefix>4.0</VersionPrefix>
<VersionSuffix>$([System.DateTime]::Now.ToString(`yyyy.MMdd`))</VersionSuffix>
<Version>$(VersionPrefix).$(VersionSuffix)</Version>
<LangVersion>latest</LangVersion>
diff --git a/NewLife.Remoting/Locales/da.yml b/NewLife.Remoting/Locales/da.yml
index e948035..d214f31 100644
--- a/NewLife.Remoting/Locales/da.yml
+++ b/NewLife.Remoting/Locales/da.yml
@@ -14,8 +14,8 @@
"{0} has {1} available services:": "{0} har {1} tilgængelige tjenester:"
"Neither Server nor Port is specified!": "Hverken Server eller Port er angivet!"
"Starting [{0}]": "Starter [{0}]"
-"Encoder: {0}": "Encoder: {0}"
-"Handler: {0}": "Handler: {0}"
+"Encoder: {0}": "Koder: {0}"
+"Handler: {0}": "Behandler: {0}"
"HTTP port [{0}] failed to start, falling back to a random port: {1}": "HTTP-porten [{0}] kunne ikke starte, falder tilbage til en tilfældig port: {1}"
"Cannot create HTTP server on port [{0}]": "Kan ikke oprette HTTP-server på port [{0}]"
"HTTP service port: {0}": "HTTP-tjenesteport: {0}"
@@ -28,7 +28,7 @@
"No server address is configured in Servers": "Der er ikke konfigureret nogen serveradresse i Servers"
"Cluster balancing: {0}": "Klyngeudligning: {0}"
"Cluster connecting: {0}": "Klyngeforbindelse: {0}"
-"Cluster failover: {0}": "Klynge-failover: {0}"
+"Cluster failover: {0}": "Klynge-fejlomkobling: {0}"
"{0} requires runtime dynamic code generation, which is not supported in the current environment (e.g. NativeAOT); use the static path pre-generated by the source generator instead.": "{0} kræver dynamisk kodegenerering under kørsel, hvilket ikke understøttes i det aktuelle miljø (f.eks. NativeAOT); brug den statiske sti, som kildegeneratoren har oprettet på forhånd."
"value type streaming return IAsyncEnumerable<{0}>": "streaming-retur af værditypen IAsyncEnumerable<{0}>"
"Cannot find service named [{0}]!": "Kan ikke finde tjenesten med navnet [{0}]!"
@@ -129,7 +129,7 @@
"NewLife Remoting Library": "NewLife Remoting-bibliotek"
"Unified RPC, HTTP, WebSocket and SRMP communication for client-server and IoT applications.": "Ensartet RPC-, HTTP-, WebSocket- og SRMP-kommunikation til klient-server- og IoT-applikationer."
"Load balancing": "Lastbalancering"
-"Failover": "Failover"
+"Failover": "Fejlomkobling"
"Partial package": "Delvis pakke"
"Standard package": "Standardpakke"
"Full package": "Fuld pakke"
diff --git a/NewLife.Remoting/Locales/de.yml b/NewLife.Remoting/Locales/de.yml
index ef9b290..0ead2b9 100644
--- a/NewLife.Remoting/Locales/de.yml
+++ b/NewLife.Remoting/Locales/de.yml
@@ -14,8 +14,8 @@
"{0} has {1} available services:": "{0} hat {1} verfügbare Dienste:"
"Neither Server nor Port is specified!": "Weder Server noch Port ist angegeben!"
"Starting [{0}]": "Starte [{0}]"
-"Encoder: {0}": "Encoder: {0}"
-"Handler: {0}": "Handler: {0}"
+"Encoder: {0}": "Kodierer: {0}"
+"Handler: {0}": "Verarbeiter: {0}"
"HTTP port [{0}] failed to start, falling back to a random port: {1}": "HTTP-Port [{0}] konnte nicht gestartet werden, Ausweichen auf einen zufälligen Port: {1}"
"Cannot create HTTP server on port [{0}]": "HTTP-Server kann nicht auf Port [{0}] erstellt werden"
"HTTP service port: {0}": "HTTP-Dienstport: {0}"
@@ -28,7 +28,7 @@
"No server address is configured in Servers": "In Servers ist keine Serveradresse konfiguriert"
"Cluster balancing: {0}": "Cluster-Ausgleich: {0}"
"Cluster connecting: {0}": "Cluster-Verbindung: {0}"
-"Cluster failover: {0}": "Cluster-Failover: {0}"
+"Cluster failover: {0}": "Cluster-Ausfallumschaltung: {0}"
"{0} requires runtime dynamic code generation, which is not supported in the current environment (e.g. NativeAOT); use the static path pre-generated by the source generator instead.": "{0} benötigt dynamische Codegenerierung zur Laufzeit, die in der aktuellen Umgebung (z. B. NativeAOT) nicht unterstützt wird; bitte den vom Quellgenerator vorab erzeugten statischen Pfad verwenden."
"value type streaming return IAsyncEnumerable<{0}>": "Streaming-Rückgabe des Werttyps IAsyncEnumerable<{0}>"
"Cannot find service named [{0}]!": "Dienst mit dem Namen [{0}] wurde nicht gefunden!"
@@ -129,7 +129,7 @@
"NewLife Remoting Library": "NewLife Remoting-Bibliothek"
"Unified RPC, HTTP, WebSocket and SRMP communication for client-server and IoT applications.": "Einheitliche RPC-, HTTP-, WebSocket- und SRMP-Kommunikation für Client-Server- und IoT-Anwendungen."
"Load balancing": "Lastverteilung"
-"Failover": "Failover"
+"Failover": "Ausfallumschaltung"
"Partial package": "Teilpaket"
"Standard package": "Standardpaket"
"Full package": "Vollpaket"
diff --git a/NewLife.Remoting/Locales/id.yml b/NewLife.Remoting/Locales/id.yml
index 908253f..d9d2a91 100644
--- a/NewLife.Remoting/Locales/id.yml
+++ b/NewLife.Remoting/Locales/id.yml
@@ -14,8 +14,8 @@
"{0} has {1} available services:": "{0} memiliki {1} layanan tersedia:"
"Neither Server nor Port is specified!": "Baik Server maupun Port tidak ditentukan!"
"Starting [{0}]": "Memulai [{0}]"
-"Encoder: {0}": "Encoder: {0}"
-"Handler: {0}": "Handler: {0}"
+"Encoder: {0}": "Pengode: {0}"
+"Handler: {0}": "Penangan: {0}"
"HTTP port [{0}] failed to start, falling back to a random port: {1}": "Port HTTP [{0}] gagal dimulai, beralih ke port acak: {1}"
"Cannot create HTTP server on port [{0}]": "Tidak dapat membuat server HTTP pada port [{0}]"
"HTTP service port: {0}": "Port layanan HTTP: {0}"
@@ -28,7 +28,7 @@
"No server address is configured in Servers": "Tidak ada alamat server yang dikonfigurasi di Servers"
"Cluster balancing: {0}": "Penyeimbangan klaster: {0}"
"Cluster connecting: {0}": "Koneksi klaster: {0}"
-"Cluster failover: {0}": "Failover klaster: {0}"
+"Cluster failover: {0}": "Pengalihan otomatis klaster: {0}"
"{0} requires runtime dynamic code generation, which is not supported in the current environment (e.g. NativeAOT); use the static path pre-generated by the source generator instead.": "{0} memerlukan pembuatan kode dinamis saat runtime, yang tidak didukung di lingkungan saat ini (misalnya NativeAOT); gunakan jalur statis yang telah dibuat sebelumnya oleh generator sumber."
"value type streaming return IAsyncEnumerable<{0}>": "pengembalian streaming tipe nilai IAsyncEnumerable<{0}>"
"Cannot find service named [{0}]!": "Tidak dapat menemukan layanan bernama [{0}]!"
@@ -129,7 +129,7 @@
"NewLife Remoting Library": "Pustaka NewLife Remoting"
"Unified RPC, HTTP, WebSocket and SRMP communication for client-server and IoT applications.": "Komunikasi RPC, HTTP, WebSocket, dan SRMP terpadu untuk aplikasi klien-server dan IoT."
"Load balancing": "Penyeimbangan beban"
-"Failover": "Failover"
+"Failover": "Pengalihan otomatis"
"Partial package": "Paket sebagian"
"Standard package": "Paket standar"
"Full package": "Paket penuh"
diff --git a/NewLife.Remoting/Locales/it.yml b/NewLife.Remoting/Locales/it.yml
index 0d56c27..0eb2161 100644
--- a/NewLife.Remoting/Locales/it.yml
+++ b/NewLife.Remoting/Locales/it.yml
@@ -14,8 +14,8 @@
"{0} has {1} available services:": "{0} ha {1} servizi disponibili:"
"Neither Server nor Port is specified!": "Né Server né Port sono specificati!"
"Starting [{0}]": "Avvio di [{0}]"
-"Encoder: {0}": "Encoder: {0}"
-"Handler: {0}": "Handler: {0}"
+"Encoder: {0}": "Codificatore: {0}"
+"Handler: {0}": "Gestore: {0}"
"HTTP port [{0}] failed to start, falling back to a random port: {1}": "La porta HTTP [{0}] non è riuscita ad avviarsi, si passa a una porta casuale: {1}"
"Cannot create HTTP server on port [{0}]": "Impossibile creare il server HTTP sulla porta [{0}]"
"HTTP service port: {0}": "Porta del servizio HTTP: {0}"
@@ -28,7 +28,7 @@
"No server address is configured in Servers": "Nessun indirizzo server configurato in Servers"
"Cluster balancing: {0}": "Bilanciamento del cluster: {0}"
"Cluster connecting: {0}": "Connessione al cluster: {0}"
-"Cluster failover: {0}": "Failover del cluster: {0}"
+"Cluster failover: {0}": "Commutazione automatica del cluster: {0}"
"{0} requires runtime dynamic code generation, which is not supported in the current environment (e.g. NativeAOT); use the static path pre-generated by the source generator instead.": "{0} richiede la generazione dinamica di codice a runtime, non supportata nell'ambiente corrente (ad esempio NativeAOT); usare il percorso statico pregenerato dal generatore di codice."
"value type streaming return IAsyncEnumerable<{0}>": "ritorno in streaming di tipo valore IAsyncEnumerable<{0}>"
"Cannot find service named [{0}]!": "Impossibile trovare il servizio denominato [{0}]!"
@@ -129,7 +129,7 @@
"NewLife Remoting Library": "Libreria NewLife Remoting"
"Unified RPC, HTTP, WebSocket and SRMP communication for client-server and IoT applications.": "Comunicazione unificata RPC, HTTP, WebSocket e SRMP per applicazioni client-server e IoT."
"Load balancing": "Bilanciamento del carico"
-"Failover": "Failover"
+"Failover": "Commutazione automatica"
"Partial package": "Pacchetto parziale"
"Standard package": "Pacchetto standard"
"Full package": "Pacchetto completo"
diff --git a/NewLife.Remoting/Locales/ms.yml b/NewLife.Remoting/Locales/ms.yml
index 4606564..62f5a10 100644
--- a/NewLife.Remoting/Locales/ms.yml
+++ b/NewLife.Remoting/Locales/ms.yml
@@ -28,7 +28,7 @@
"No server address is configured in Servers": "Tiada alamat pelayan dikonfigurasikan dalam Servers"
"Cluster balancing: {0}": "Pengimbangan kelompok: {0}"
"Cluster connecting: {0}": "Sambungan kelompok: {0}"
-"Cluster failover: {0}": "Peralihan kelompok: {0}"
+"Cluster failover: {0}": "Pengalihan automatik kelompok: {0}"
"{0} requires runtime dynamic code generation, which is not supported in the current environment (e.g. NativeAOT); use the static path pre-generated by the source generator instead.": "{0} memerlukan penjanaan kod dinamik semasa runtime, yang tidak disokong dalam persekitaran semasa (contohnya NativeAOT); gunakan laluan statik yang dijana terlebih dahulu oleh penjana sumber."
"value type streaming return IAsyncEnumerable<{0}>": "pemulangan penstriman jenis nilai IAsyncEnumerable<{0}>"
"Cannot find service named [{0}]!": "Tidak dapat menemui perkhidmatan bernama [{0}]!"
@@ -129,7 +129,7 @@
"NewLife Remoting Library": "Pustaka NewLife Remoting"
"Unified RPC, HTTP, WebSocket and SRMP communication for client-server and IoT applications.": "Komunikasi RPC, HTTP, WebSocket dan SRMP bersepadu untuk aplikasi klien-pelayan dan IoT."
"Load balancing": "Pengimbangan beban"
-"Failover": "Failover"
+"Failover": "Pengalihan automatik"
"Partial package": "Pakej separa"
"Standard package": "Pakej standard"
"Full package": "Pakej penuh"
diff --git a/NewLife.Remoting/Locales/nb.yml b/NewLife.Remoting/Locales/nb.yml
index 9576b7e..c2879eb 100644
--- a/NewLife.Remoting/Locales/nb.yml
+++ b/NewLife.Remoting/Locales/nb.yml
@@ -28,7 +28,7 @@
"No server address is configured in Servers": "Ingen serveradresse er konfigurert i Servers"
"Cluster balancing: {0}": "Klyngeutjevning: {0}"
"Cluster connecting: {0}": "Klyngeforbindelse: {0}"
-"Cluster failover: {0}": "Klynge-failover: {0}"
+"Cluster failover: {0}": "Klynge-feiloverføring: {0}"
"{0} requires runtime dynamic code generation, which is not supported in the current environment (e.g. NativeAOT); use the static path pre-generated by the source generator instead.": "{0} krever dynamisk kodegenerering under kjøring, som ikke støttes i gjeldende miljø (f.eks. NativeAOT); bruk den statiske banen som kildegeneratoren har laget på forhånd."
"value type streaming return IAsyncEnumerable<{0}>": "strømmende retur av verditypen IAsyncEnumerable<{0}>"
"Cannot find service named [{0}]!": "Finner ikke tjenesten med navnet [{0}]!"
@@ -129,7 +129,7 @@
"NewLife Remoting Library": "NewLife Remoting-bibliotek"
"Unified RPC, HTTP, WebSocket and SRMP communication for client-server and IoT applications.": "Enhetlig RPC-, HTTP-, WebSocket- og SRMP-kommunikasjon for klient-tjener- og IoT-applikasjoner."
"Load balancing": "Lastbalansering"
-"Failover": "Failover"
+"Failover": "Feiloverføring"
"Partial package": "Delvis pakke"
"Standard package": "Standardpakke"
"Full package": "Full pakke"
diff --git a/NewLife.Remoting/Locales/nl.yml b/NewLife.Remoting/Locales/nl.yml
index 1c7dd8c..c127818 100644
--- a/NewLife.Remoting/Locales/nl.yml
+++ b/NewLife.Remoting/Locales/nl.yml
@@ -14,8 +14,8 @@
"{0} has {1} available services:": "{0} heeft {1} beschikbare services:"
"Neither Server nor Port is specified!": "Noch Server noch Port is opgegeven!"
"Starting [{0}]": "Starten van [{0}]"
-"Encoder: {0}": "Encoder: {0}"
-"Handler: {0}": "Handler: {0}"
+"Encoder: {0}": "Codeerder: {0}"
+"Handler: {0}": "Afhandelaar: {0}"
"HTTP port [{0}] failed to start, falling back to a random port: {1}": "HTTP-poort [{0}] kon niet starten, terugval op een willekeurige poort: {1}"
"Cannot create HTTP server on port [{0}]": "Kan geen HTTP-server maken op poort [{0}]"
"HTTP service port: {0}": "HTTP-servicepoort: {0}"
@@ -28,7 +28,7 @@
"No server address is configured in Servers": "Er is geen serveradres geconfigureerd in Servers"
"Cluster balancing: {0}": "Clusterbalancering: {0}"
"Cluster connecting: {0}": "Clusterverbinding: {0}"
-"Cluster failover: {0}": "Cluster-failover: {0}"
+"Cluster failover: {0}": "Automatische clusteromschakeling: {0}"
"{0} requires runtime dynamic code generation, which is not supported in the current environment (e.g. NativeAOT); use the static path pre-generated by the source generator instead.": "{0} vereist dynamische codegeneratie tijdens runtime, wat niet wordt ondersteund in de huidige omgeving (bijvoorbeeld NativeAOT); gebruik het statische pad dat vooraf door de broncodegenerator is gemaakt."
"value type streaming return IAsyncEnumerable<{0}>": "streaming-retour van waardetype IAsyncEnumerable<{0}>"
"Cannot find service named [{0}]!": "Kan service met de naam [{0}] niet vinden!"
@@ -129,7 +129,7 @@
"NewLife Remoting Library": "NewLife Remoting-bibliotheek"
"Unified RPC, HTTP, WebSocket and SRMP communication for client-server and IoT applications.": "Uniforme RPC-, HTTP-, WebSocket- en SRMP-communicatie voor client-server- en IoT-toepassingen."
"Load balancing": "Taakverdeling"
-"Failover": "Failover"
+"Failover": "Automatische omschakeling"
"Partial package": "Gedeeltelijk pakket"
"Standard package": "Standaardpakket"
"Full package": "Volledig pakket"
diff --git a/NewLife.Remoting/Locales/pt.yml b/NewLife.Remoting/Locales/pt.yml
index 272b115..d16aca0 100644
--- a/NewLife.Remoting/Locales/pt.yml
+++ b/NewLife.Remoting/Locales/pt.yml
@@ -28,7 +28,7 @@
"No server address is configured in Servers": "Nenhum endereço de servidor configurado em Servers"
"Cluster balancing: {0}": "Balanceamento do cluster: {0}"
"Cluster connecting: {0}": "Conexão do cluster: {0}"
-"Cluster failover: {0}": "Failover do cluster: {0}"
+"Cluster failover: {0}": "Comutação automática do cluster: {0}"
"{0} requires runtime dynamic code generation, which is not supported in the current environment (e.g. NativeAOT); use the static path pre-generated by the source generator instead.": "{0} requer geração dinâmica de código em tempo de execução, que não é suportada no ambiente atual (por exemplo NativeAOT); use o caminho estático pré-gerado pelo gerador de código."
"value type streaming return IAsyncEnumerable<{0}>": "retorno de streaming de tipo de valor IAsyncEnumerable<{0}>"
"Cannot find service named [{0}]!": "Não foi possível encontrar o serviço chamado [{0}]!"
@@ -129,7 +129,7 @@
"NewLife Remoting Library": "Biblioteca NewLife Remoting"
"Unified RPC, HTTP, WebSocket and SRMP communication for client-server and IoT applications.": "Comunicação unificada RPC, HTTP, WebSocket e SRMP para aplicações cliente-servidor e IoT."
"Load balancing": "Balanceamento de carga"
-"Failover": "Failover"
+"Failover": "Comutação automática"
"Partial package": "Pacote parcial"
"Standard package": "Pacote padrão"
"Full package": "Pacote completo"
diff --git a/NewLife.Remoting/Locales/ro.yml b/NewLife.Remoting/Locales/ro.yml
index 2ae9d86..8f39295 100644
--- a/NewLife.Remoting/Locales/ro.yml
+++ b/NewLife.Remoting/Locales/ro.yml
@@ -14,8 +14,8 @@
"{0} has {1} available services:": "{0} are {1} servicii disponibile:"
"Neither Server nor Port is specified!": "Nu este specificat nici Server, nici Port!"
"Starting [{0}]": "Se pornește [{0}]"
-"Encoder: {0}": "Encoder: {0}"
-"Handler: {0}": "Handler: {0}"
+"Encoder: {0}": "Codificator: {0}"
+"Handler: {0}": "Gestionar: {0}"
"HTTP port [{0}] failed to start, falling back to a random port: {1}": "Portul HTTP [{0}] nu a putut porni, se folosește un port aleatoriu: {1}"
"Cannot create HTTP server on port [{0}]": "Nu se poate crea serverul HTTP pe portul [{0}]"
"HTTP service port: {0}": "Portul serviciului HTTP: {0}"
diff --git a/NewLife.Remoting/Locales/sv.yml b/NewLife.Remoting/Locales/sv.yml
index 3cc78b0..19cc2d7 100644
--- a/NewLife.Remoting/Locales/sv.yml
+++ b/NewLife.Remoting/Locales/sv.yml
@@ -28,7 +28,7 @@
"No server address is configured in Servers": "Ingen serveradress har konfigurerats i Servers"
"Cluster balancing: {0}": "Klusterutjämning: {0}"
"Cluster connecting: {0}": "Klusteranslutning: {0}"
-"Cluster failover: {0}": "Kluster-omkoppling: {0}"
+"Cluster failover: {0}": "Kluster-felväxling: {0}"
"{0} requires runtime dynamic code generation, which is not supported in the current environment (e.g. NativeAOT); use the static path pre-generated by the source generator instead.": "{0} kräver dynamisk kodgenerering vid körning, vilket inte stöds i den aktuella miljön (t.ex. NativeAOT); använd den statiska sökväg som källgeneratorn skapat i förväg."
"value type streaming return IAsyncEnumerable<{0}>": "strömmande retur av värdetypen IAsyncEnumerable<{0}>"
"Cannot find service named [{0}]!": "Det går inte att hitta tjänsten med namnet [{0}]!"
@@ -129,7 +129,7 @@
"NewLife Remoting Library": "NewLife Remoting-bibliotek"
"Unified RPC, HTTP, WebSocket and SRMP communication for client-server and IoT applications.": "Enhetlig RPC-, HTTP-, WebSocket- och SRMP-kommunikation för klient-server- och IoT-program."
"Load balancing": "Lastbalansering"
-"Failover": "Failover"
+"Failover": "Felväxling"
"Partial package": "Delvis paket"
"Standard package": "Standardpaket"
"Full package": "Komplett paket"
diff --git a/NewLife.Remoting/NewLife.Remoting.csproj b/NewLife.Remoting/NewLife.Remoting.csproj
index e3b0476..d6a577e 100644
--- a/NewLife.Remoting/NewLife.Remoting.csproj
+++ b/NewLife.Remoting/NewLife.Remoting.csproj
@@ -7,7 +7,7 @@
<Description>Unified RPC, HTTP, WebSocket and SRMP communication for client-server and IoT applications.</Description>
<Company>新生命开发团队</Company>
<Copyright>©2002-2026 新生命开发团队</Copyright>
- <VersionPrefix>3.9</VersionPrefix>
+ <VersionPrefix>4.0</VersionPrefix>
<VersionSuffix>$([System.DateTime]::Now.ToString(`yyyy.MMdd`))</VersionSuffix>
<Version>$(VersionPrefix).$(VersionSuffix)</Version>
<FileVersion>$(Version)</FileVersion>
@@ -33,9 +33,9 @@
<PackageIcon>leaf.png</PackageIcon>
<RepositoryUrl>https://github.com/NewLifeX/NewLife.Remoting</RepositoryUrl>
<RepositoryType>git</RepositoryType>
- <PackageTags>新生命团队;X组件;NewLife;$(AssemblyName)</PackageTags>
+ <PackageTags>新生命团队;X组件;NewLife;RPC;HTTP;WebSocket;SRMP;IoT;NativeAOT;AOT;$(AssemblyName)</PackageTags>
<PackageDescription>Unified RPC, HTTP, WebSocket and SRMP communication for client-server and IoT applications. 提供 RPC、HTTP、WebSocket 与 SRMP 统一通信能力。</PackageDescription>
- <PackageReleaseNotes>登录失败自动重试,配合滚动登录超时设计,服务端冷启动时自动恢复;ZeroServer/IoTZero 示例客户端改用 Open 规范用法,响应总线自测恢复执行;消除编译警告;升级 NewLife.Core 依赖</PackageReleaseNotes>
+ <PackageReleaseNotes>v4 大版本:对齐 NewLife.Core v12 网络层,主端口仅跑 SRMP、HTTP 需显式 HttpPort,移除 ws:// 承载 SRMP 与自研 HTTP/WS 帧栈;支持 NativeAOT 并内置源生成器,动作静态表消除路由反射;新增令牌吊销与访问/刷新令牌分离,GetUserHost 支持可信代理防伪造;Extensions 新增最小 API 端点宿主;运行时词表扩展至 31 种语言;升级 NewLife.Core 12.0.2026.1024</PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
@@ -71,7 +71,7 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="NewLife.Core" Version="12.0.2026.1021-beta0321" />
+ <PackageReference Include="NewLife.Core" Version="12.0.2026.1024" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net45' or '$(TargetFramework)'=='net461'">
diff --git a/README.ar.md b/README.ar.md
index 4499868..47a1631 100644
--- a/README.ar.md
+++ b/README.ar.md
@@ -114,6 +114,18 @@ client.Headers["TenantId"] = "tenant-01";
| زمن استدعاء RPC واحد | ~41 ميكروثانية | — |
| الأطر المدعومة | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0 (متوافق مع NewLife.Core v12، وNativeAOT من net8.0) |
+## الترقية إلى v4
+
+يتوافق v4 مع NewLife.Core v12 ويغيّر طبقة الشبكة. قبل الترقية:
+
+- **منفذان**: المنفذ الرئيسي يخدم SRMP فقط؛ عيّن `HttpPort` صراحةً لتمكين HTTP (معطّل افتراضيًا).
+- **لا SRMP عبر WebSocket**: عناوين `ws://`/`wss://` أصبحت ترمي `NotSupportedException`؛ استخدم `tcp://`/`udp://` لـ SRMP و`http(s)://` لاستدعاءات HTTP.
+- **إزالة أنواع الترميز المخصصة**: حُذفت `WsClient` و`HttpCodec` و`WebSocketClientCodec` و`WebSocketServerCodec` وطرق الإطار في `HttpMessage`؛ يتولى `HttpServer` من Core معالجة HTTP، ويجب تنفيذ `IMessageCodec` للتأطير المخصص.
+- **مفاتيح الرسائل بالإنجليزية**: تستخدم رسائل وقت التشغيل النص الإنجليزي كمفتاح؛ قارن `ApiCode` أو رموز الأخطاء بدلًا من النص المترجم.
+- **أطر العمل المستهدفة**: لم تعد أصول net5.0/net7.0/net9.0 تُشحن؛ يعود NuGet تلقائيًا إلى أقرب إطار مدعوم.
+
+راجع [ChangeLog.md](ChangeLog.md) للقائمة الكاملة.
+
---
## التعاون مع مكوّنات NewLife الأخرى
diff --git a/README.de.md b/README.de.md
index 678da85..7f2c2d8 100644
--- a/README.de.md
+++ b/README.de.md
@@ -114,6 +114,18 @@ client.Headers["TenantId"] = "tenant-01";
| Latenz pro RPC | ~41 μs | — |
| Zielframeworks | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0 (abgestimmt auf NewLife.Core v12; NativeAOT ab net8.0) |
+## Upgrade auf v4
+
+v4 ist auf NewLife.Core v12 abgestimmt und ändert die Netzwerkschicht. Vor dem Upgrade:
+
+- **Zwei Ports**: Der Hauptport bedient nur SRMP; HTTP muss explizit über `HttpPort` aktiviert werden (standardmäßig aus).
+- **Kein SRMP über WebSocket**: `ws://`/`wss://`-Adressen lösen jetzt `NotSupportedException` aus; verwenden Sie `tcp://`/`udp://` für SRMP und `http(s)://` für HTTP-Aufrufe.
+- **Codec-Typen entfernt**: `WsClient`, `HttpCodec`, `WebSocketClientCodec`, `WebSocketServerCodec` und die Frame-Methoden von `HttpMessage` entfallen; HTTP übernimmt der Core-`HttpServer`, benutzerdefiniertes Framing implementiert `IMessageCodec`.
+- **Englische Nachrichtenschlüssel**: Laufzeitmeldungen verwenden den englischen Quelltext als Schlüssel; vergleichen Sie `ApiCode` oder Fehlercodes statt lokalisierter Texte.
+- **Zielframeworks**: net5.0/net7.0/net9.0-Assets werden nicht mehr ausgeliefert; NuGet fällt automatisch auf das nächstliegende unterstützte Framework zurück.
+
+Die vollständige Liste steht in [ChangeLog.md](ChangeLog.md).
+
---
## Zusammenspiel mit anderen NewLife-Komponenten
diff --git a/README.es.md b/README.es.md
index ec2e52c..66f48d8 100644
--- a/README.es.md
+++ b/README.es.md
@@ -114,6 +114,18 @@ client.Headers["TenantId"] = "tenant-01";
| Latencia por RPC | ~41 μs | — |
| Frameworks | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0 (alineado con NewLife.Core v12; NativeAOT desde net8.0) |
+## Actualización a v4
+
+v4 se alinea con NewLife.Core v12 y cambia la capa de red. Antes de actualizar:
+
+- **Dos puertos**: el puerto principal solo sirve SRMP; configure `HttpPort` explícitamente para habilitar HTTP (desactivado por defecto).
+- **Sin SRMP sobre WebSocket**: las direcciones `ws://`/`wss://` ahora lanzan `NotSupportedException`; use `tcp://`/`udp://` para SRMP y `http(s)://` para llamadas HTTP.
+- **Tipos de códec eliminados**: `WsClient`, `HttpCodec`, `WebSocketClientCodec`, `WebSocketServerCodec` y los métodos de trama de `HttpMessage` ya no existen; HTTP lo gestiona el `HttpServer` de Core y el framing personalizado debe implementar `IMessageCodec`.
+- **Claves de mensajes en inglés**: los mensajes en tiempo de ejecución usan el texto inglés como clave; compare `ApiCode` o códigos de error en lugar del texto localizado.
+- **Frameworks de destino**: ya no se publican los activos net5.0/net7.0/net9.0; NuGet recurre automáticamente al framework compatible más cercano.
+
+Consulte [ChangeLog.md](ChangeLog.md) para la lista completa.
+
---
## Integración con otros componentes NewLife
diff --git a/README.fr.md b/README.fr.md
index b81a20a..9205442 100644
--- a/README.fr.md
+++ b/README.fr.md
@@ -114,6 +114,18 @@ client.Headers["TenantId"] = "tenant-01";
| Latence par RPC | ~41 μs | — |
| Frameworks | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0 (aligné sur NewLife.Core v12 ; NativeAOT à partir de net8.0) |
+## Mise à niveau vers v4
+
+v4 s'aligne sur NewLife.Core v12 et modifie la couche réseau. Avant de mettre à niveau :
+
+- **Deux ports** : le port principal ne sert que SRMP ; définissez `HttpPort` explicitement pour activer HTTP (désactivé par défaut).
+- **Plus de SRMP sur WebSocket** : les adresses `ws://`/`wss://` lèvent désormais `NotSupportedException` ; utilisez `tcp://`/`udp://` pour SRMP et `http(s)://` pour les appels HTTP.
+- **Types de codec supprimés** : `WsClient`, `HttpCodec`, `WebSocketClientCodec`, `WebSocketServerCodec` et les méthodes de trame de `HttpMessage` disparaissent ; HTTP est géré par le `HttpServer` de Core, et un framing personnalisé doit implémenter `IMessageCodec`.
+- **Clés de messages en anglais** : les messages d'exécution utilisent le texte anglais comme clé ; comparez `ApiCode` ou les codes d'erreur au lieu du texte localisé.
+- **Frameworks cibles** : les assets net5.0/net7.0/net9.0 ne sont plus fournis ; NuGet bascule automatiquement vers le framework compatible le plus proche.
+
+Voir [ChangeLog.md](ChangeLog.md) pour la liste complète.
+
---
## Coopération avec les autres composants NewLife
diff --git a/README.ja.md b/README.ja.md
index 05bea5c..7303365 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -114,6 +114,18 @@ client.Headers["TenantId"] = "tenant-01";
| RPC あたりの遅延 | 約 41 μs | — |
| 対応フレームワーク | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0(NewLife.Core v12 に追随、net8.0+ で NativeAOT) |
+## v4 へのアップグレード
+
+v4 は NewLife.Core v12 に追随し、ネットワーク層を変更します。アップグレード前にご確認ください:
+
+- **デュアルポート**:メインポートは SRMP 専用です。HTTP を有効にするには `HttpPort` を明示的に設定してください(既定では無効)。
+- **WebSocket 上の SRMP を廃止**:`ws://`/`wss://` アドレスは `NotSupportedException` をスローします。SRMP には `tcp://`/`udp://`、HTTP 呼び出しには `http(s)://` を使用してください。
+- **独自コーデック型を削除**:`WsClient`、`HttpCodec`、`WebSocketClientCodec`、`WebSocketServerCodec`、`HttpMessage` のフレームメソッドは削除されました。HTTP は Core の `HttpServer` が処理し、独自フレーミングは `IMessageCodec` を実装してください。
+- **英語のメッセージキー**:実行時メッセージは英語原文をキーにします。ローカライズ済み文字列ではなく `ApiCode` またはエラーコードで判定してください。
+- **ターゲットフレームワーク**:net5.0/net7.0/net9.0 のアセットは提供されなくなり、NuGet は最も近いサポート対象フレームワークへ自動的にフォールバックします。
+
+完全な一覧は [ChangeLog.md](ChangeLog.md) を参照してください。
+
---
## 他の NewLife コンポーネントとの連携
diff --git a/README.ko.md b/README.ko.md
index d5fdaad..e2367ac 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -114,6 +114,18 @@ client.Headers["TenantId"] = "tenant-01";
| RPC 1회 지연 | 약 41 μs | — |
| 프레임워크 지원 | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0 (NewLife.Core v12 기준, net8.0+ NativeAOT) |
+## v4 업그레이드
+
+v4는 NewLife.Core v12에 맞춰 네트워크 계층을 변경합니다. 업그레이드 전에 확인하세요:
+
+- **이중 포트**: 기본 포트는 SRMP만 처리합니다. HTTP를 사용하려면 `HttpPort`를 명시적으로 설정하세요(기본적으로 꺼짐).
+- **WebSocket 기반 SRMP 제거**: `ws://`/`wss://` 주소는 이제 `NotSupportedException`을 발생시킵니다. SRMP에는 `tcp://`/`udp://`, HTTP 호출에는 `http(s)://`를 사용하세요.
+- **자체 코덱 형식 제거**: `WsClient`, `HttpCodec`, `WebSocketClientCodec`, `WebSocketServerCodec` 및 `HttpMessage` 프레임 메서드가 삭제되었습니다. HTTP는 Core `HttpServer`가 처리하며, 사용자 지정 프레이밍은 `IMessageCodec`을 구현해야 합니다.
+- **영문 메시지 키**: 런타임 메시지는 영문 원문을 키로 사용합니다. 지역화된 문구 대신 `ApiCode` 또는 오류 코드로 판별하세요.
+- **대상 프레임워크**: net5.0/net7.0/net9.0 자산은 더 이상 제공되지 않으며, NuGet은 가장 가까운 지원 프레임워크로 자동 대체합니다.
+
+전체 목록은 [ChangeLog.md](ChangeLog.md)를 참조하세요.
+
---
## 다른 NewLife 구성 요소와의 협업
diff --git a/README.md b/README.md
index 319c50a..0a6f337 100644
--- a/README.md
+++ b/README.md
@@ -112,6 +112,18 @@ client.Headers["TenantId"] = "tenant-01";
| Single RPC latency | ~41 μs | — |
| Framework support | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0 (aligned with NewLife.Core v12; NativeAOT on net8.0+) |
+## Upgrading to v4
+
+v4 aligns with NewLife.Core v12 and changes the network layering. Before upgrading:
+
+- **Dual ports**: the main port serves SRMP only; set `HttpPort` explicitly to enable HTTP (disabled by default).
+- **No SRMP over WebSocket**: `ws://`/`wss://` addresses now throw `NotSupportedException`; use `tcp://`/`udp://` for SRMP and `http(s)://` for HTTP calls.
+- **Codec types removed**: `WsClient`, `HttpCodec`, `WebSocketClientCodec`, `WebSocketServerCodec` and the `HttpMessage` frame methods are gone; HTTP is handled by Core `HttpServer`, and custom framing should implement `IMessageCodec`.
+- **English message keys**: runtime messages use English source text as locale keys; match `ApiCode` or error codes instead of localized text.
+- **Target frameworks**: net5.0/net7.0/net9.0 assets are no longer shipped; NuGet automatically falls back to the nearest supported framework.
+
+See [ChangeLog.md](ChangeLog.md) for the complete list.
+
---
## Working with Other NewLife Components
@@ -178,7 +190,7 @@ NewLife is a layered ecosystem of independently versioned open-source projects.
| [GitCandy](https://github.com/NewLifeX/GitCandy) | Self-hosted Git source code management system. | — |
| [LuckyClover](https://github.com/NewLifeX/LuckyClover) | Installs the matching .NET runtime automatically when launched from an older host. | — |
-> Badges show the primary package of each project; theme, extension and satellite packages are listed on their NuGet pages.The 30+ packages not listed here (FTP, LoRa, Office extensions, protocol drivers and more) are available under the [NewLifeX organization](https://github.com/NewLifeX) and on [newlifex.com](https://newlifex.com).
+> Badges show the primary package of each project; theme, extension and satellite packages are listed on their NuGet pages. The 30+ packages not listed here (FTP, LoRa, Office extensions, protocol drivers and more) are available under the [NewLifeX organization](https://github.com/NewLifeX) and on [newlifex.com](https://newlifex.com).
---
## Team
diff --git a/README.nuget.md b/README.nuget.md
index d7f7877..82c84d0 100644
--- a/README.nuget.md
+++ b/README.nuget.md
@@ -114,6 +114,18 @@ client.Headers["TenantId"] = "tenant-01";
| Single RPC latency | ~41 μs | — |
| Framework support | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0 (aligned with NewLife.Core v12; NativeAOT on net8.0+) |
+## Upgrading to v4
+
+v4 aligns with NewLife.Core v12 and changes the network layering. Before upgrading:
+
+- **Dual ports**: the main port serves SRMP only; set `HttpPort` explicitly to enable HTTP (disabled by default).
+- **No SRMP over WebSocket**: `ws://`/`wss://` addresses now throw `NotSupportedException`; use `tcp://`/`udp://` for SRMP and `http(s)://` for HTTP calls.
+- **Codec types removed**: `WsClient`, `HttpCodec`, `WebSocketClientCodec`, `WebSocketServerCodec` and the `HttpMessage` frame methods are gone; HTTP is handled by Core `HttpServer`, and custom framing should implement `IMessageCodec`.
+- **English message keys**: runtime messages use English source text as locale keys; match `ApiCode` or error codes instead of localized text.
+- **Target frameworks**: net5.0/net7.0/net9.0 assets are no longer shipped; NuGet automatically falls back to the nearest supported framework.
+
+See [ChangeLog.md](https://github.com/NewLifeX/NewLife.Remoting/blob/master/ChangeLog.md) for the complete list.
+
---
## Working with Other NewLife Components
@@ -180,7 +192,7 @@ NewLife is a layered ecosystem of independently versioned open-source projects.
| [GitCandy](https://github.com/NewLifeX/GitCandy) | Self-hosted Git source code management system. | — |
| [LuckyClover](https://github.com/NewLifeX/LuckyClover) | Installs the matching .NET runtime automatically when launched from an older host. | — |
-> Badges show the primary package of each project; theme, extension and satellite packages are listed on their NuGet pages.The 30+ packages not listed here (FTP, LoRa, Office extensions, protocol drivers and more) are available under the [NewLifeX organization](https://github.com/NewLifeX) and on [newlifex.com](https://newlifex.com).
+> Badges show the primary package of each project; theme, extension and satellite packages are listed on their NuGet pages. The 30+ packages not listed here (FTP, LoRa, Office extensions, protocol drivers and more) are available under the [NewLifeX organization](https://github.com/NewLifeX) and on [newlifex.com](https://newlifex.com).
---
## Team
diff --git a/README.pt.md b/README.pt.md
index 085146e..747bb03 100644
--- a/README.pt.md
+++ b/README.pt.md
@@ -114,6 +114,18 @@ client.Headers["TenantId"] = "tenant-01";
| Latência por RPC | ~41 μs | — |
| Frameworks | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0 (alinhado ao NewLife.Core v12; NativeAOT a partir do net8.0) |
+## Atualização para v4
+
+O v4 alinha-se ao NewLife.Core v12 e altera a camada de rede. Antes de atualizar:
+
+- **Duas portas**: a porta principal atende apenas SRMP; defina `HttpPort` explicitamente para habilitar HTTP (desativado por padrão).
+- **Sem SRMP sobre WebSocket**: endereços `ws://`/`wss://` agora lançam `NotSupportedException`; use `tcp://`/`udp://` para SRMP e `http(s)://` para chamadas HTTP.
+- **Tipos de codec removidos**: `WsClient`, `HttpCodec`, `WebSocketClientCodec`, `WebSocketServerCodec` e os métodos de quadro de `HttpMessage` foram removidos; o HTTP é tratado pelo `HttpServer` do Core, e o enquadramento personalizado deve implementar `IMessageCodec`.
+- **Chaves de mensagens em inglês**: mensagens de runtime usam o texto inglês como chave; compare `ApiCode` ou códigos de erro em vez do texto localizado.
+- **Frameworks de destino**: os ativos net5.0/net7.0/net9.0 não são mais distribuídos; o NuGet recorre automaticamente ao framework compatível mais próximo.
+
+Consulte [ChangeLog.md](ChangeLog.md) para a lista completa.
+
---
## Cooperação com outros componentes NewLife
diff --git a/README.ru.md b/README.ru.md
index 66b77b9..71ba84b 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -114,6 +114,18 @@ client.Headers["TenantId"] = "tenant-01";
| Задержка одного RPC | ~41 мкс | — |
| Поддерживаемые платформы | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0 (в соответствии с NewLife.Core v12; NativeAOT с net8.0) |
+## Обновление до v4
+
+v4 соответствует NewLife.Core v12 и меняет сетевой уровень. Перед обновлением:
+
+- **Два порта**: основной порт обслуживает только SRMP; для HTTP явно задайте `HttpPort` (по умолчанию отключён).
+- **SRMP через WebSocket больше не поддерживается**: адреса `ws://`/`wss://` теперь вызывают `NotSupportedException`; используйте `tcp://`/`udp://` для SRMP и `http(s)://` для HTTP-вызовов.
+- **Собственные типы кодеков удалены**: `WsClient`, `HttpCodec`, `WebSocketClientCodec`, `WebSocketServerCodec` и методы кадров `HttpMessage` удалены; HTTP обрабатывает `HttpServer` из Core, а для пользовательского кадрирования реализуйте `IMessageCodec`.
+- **Английские ключи сообщений**: сообщения времени выполнения используют английский исходный текст как ключ; сравнивайте `ApiCode` или коды ошибок вместо локализованного текста.
+- **Целевые платформы**: активы net5.0/net7.0/net9.0 больше не поставляются; NuGet автоматически переходит на ближайшую поддерживаемую платформу.
+
+Полный список см. в [ChangeLog.md](ChangeLog.md).
+
---
## Совместная работа с другими компонентами NewLife
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 5d33f78..caf07a5 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -113,6 +113,18 @@ client.Headers["TenantId"] = "tenant-01";
| 单次 RPC 延迟 | ~41 μs | — |
| 框架兼容 | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0(跟随 NewLife.Core v12,net8.0+ 支持 NativeAOT) |
+## 升级到 v4
+
+v4 对齐 NewLife.Core v12 并调整网络分层,升级前请注意:
+
+- **双端口**:主端口仅承载 SRMP;HTTP 需显式设置 `HttpPort`(默认关闭)。
+- **不再支持 SRMP-over-WS**:`ws://`/`wss://` 地址会抛 `NotSupportedException`;SRMP 请用 `tcp://`/`udp://`,HTTP 调用请用 `http(s)://`。
+- **移除自研编解码类型**:`WsClient`、`HttpCodec`、`WebSocketClientCodec`、`WebSocketServerCodec` 与 `HttpMessage` 帧方法已删除;HTTP 由 Core `HttpServer` 承载,自定义帧协议请实现 `IMessageCodec`。
+- **运行时消息英文键**:异常与日志以英文原文作为词表键,请改用 `ApiCode` 或错误码判断,不再匹配本地化文案。
+- **目标框架**:不再提供 net5.0/net7.0/net9.0 资产,NuGet 会自动回退到最接近的受支持框架。
+
+完整清单见 [ChangeLog.md](ChangeLog.md)。
+
---
## 与其它 NewLife 组件协同
diff --git a/README.zh-TW.md b/README.zh-TW.md
index e6cc4ee..9284e16 100644
--- a/README.zh-TW.md
+++ b/README.zh-TW.md
@@ -113,6 +113,18 @@ client.Headers["TenantId"] = "tenant-01";
| 單次 RPC 延遲 | ~41 μs | — |
| 框架相容 | net45 / net461 / netstandard2.0 / netstandard2.1 / net6.0 / net8.0 / net10.0(跟隨 NewLife.Core v12,net8.0+ 支援 NativeAOT) |
+## 升級到 v4
+
+v4 對齊 NewLife.Core v12 並調整網路分層,升級前請注意:
+
+- **雙埠**:主埠僅承載 SRMP;HTTP 需明確設定 `HttpPort`(預設關閉)。
+- **不再支援 SRMP-over-WS**:`ws://`/`wss://` 位址會拋出 `NotSupportedException`;SRMP 請用 `tcp://`/`udp://`,HTTP 呼叫請用 `http(s)://`。
+- **移除自研編解碼類型**:`WsClient`、`HttpCodec`、`WebSocketClientCodec`、`WebSocketServerCodec` 與 `HttpMessage` 幀方法已刪除;HTTP 由 Core `HttpServer` 承載,自訂幀協定請實作 `IMessageCodec`。
+- **執行階段訊息英文鍵**:例外與日誌以英文原文作為詞表鍵,請改用 `ApiCode` 或錯誤碼判斷,不再比對本地化文案。
+- **目標框架**:不再提供 net5.0/net7.0/net9.0 資產,NuGet 會自動回退到最接近的受支援框架。
+
+完整清單見 [ChangeLog.md](ChangeLog.md)。
+
---
## 與其他 NewLife 元件協同
diff --git a/Samples/IoTZero/IoTZero.csproj b/Samples/IoTZero/IoTZero.csproj
index b1be4a1..033e94e 100644
--- a/Samples/IoTZero/IoTZero.csproj
+++ b/Samples/IoTZero/IoTZero.csproj
@@ -24,9 +24,9 @@
<ItemGroup>
<PackageReference Include="NewLife.Cube.Core" Version="6.15.2026.1018-beta1727" />
- <PackageReference Include="NewLife.IoT" Version="3.1.2026.1019-beta1517" />
- <PackageReference Include="NewLife.MQTT" Version="3.2.2026.1019-beta1517" />
- <PackageReference Include="NewLife.Redis" Version="6.6.2026.1019-beta1511" />
+ <PackageReference Include="NewLife.IoT" Version="3.1.2026.1022-beta0136" />
+ <PackageReference Include="NewLife.MQTT" Version="3.2.2026.1022-beta0206" />
+ <PackageReference Include="NewLife.Redis" Version="6.6.2026.1022-beta0149" />
<PackageReference Include="NewLife.Stardust.Extensions" Version="3.10.2026.1021-beta0432" />
<PackageReference Include="NewLife.XCode" Version="12.2.2026.1021-beta0423" />
</ItemGroup>
diff --git a/Samples/Zero.RpcServer/Zero.RpcServer.csproj b/Samples/Zero.RpcServer/Zero.RpcServer.csproj
index c7b5ce4..bc5d3e1 100644
--- a/Samples/Zero.RpcServer/Zero.RpcServer.csproj
+++ b/Samples/Zero.RpcServer/Zero.RpcServer.csproj
@@ -20,7 +20,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="NewLife.Redis" Version="6.6.2026.1019-beta1511" />
+ <PackageReference Include="NewLife.Redis" Version="6.6.2026.1022-beta0149" />
<PackageReference Include="NewLife.Stardust" Version="3.10.2026.1021-beta0432" />
<PackageReference Include="NewLife.XCode" Version="12.2.2026.1021-beta0423" />
</ItemGroup>
diff --git a/Samples/ZeroServer/ZeroServer.csproj b/Samples/ZeroServer/ZeroServer.csproj
index d04655f..11223e3 100644
--- a/Samples/ZeroServer/ZeroServer.csproj
+++ b/Samples/ZeroServer/ZeroServer.csproj
@@ -20,7 +20,7 @@
<ItemGroup>
<PackageReference Include="NewLife.Cube.Core" Version="6.15.2026.1018-beta1727" />
- <PackageReference Include="NewLife.Redis" Version="6.6.2026.1019-beta1511" />
+ <PackageReference Include="NewLife.Redis" Version="6.6.2026.1022-beta0149" />
<PackageReference Include="NewLife.Stardust.Extensions" Version="3.10.2026.1021-beta0432" />
<PackageReference Include="NewLife.XCode" Version="12.2.2026.1021-beta0423" />
</ItemGroup>
diff --git a/Test/Test.csproj b/Test/Test.csproj
index b316748..5201354 100644
--- a/Test/Test.csproj
+++ b/Test/Test.csproj
@@ -12,7 +12,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="NewLife.Core" Version="12.0.2026.1021-beta0321" />
+ <PackageReference Include="NewLife.Core" Version="12.0.2026.1024" />
</ItemGroup>
<ItemGroup>
diff --git a/Tools/Scripts/check-locales.ps1 b/Tools/Scripts/check-locales.ps1
index a3e49b1..7e57cf4 100644
--- a/Tools/Scripts/check-locales.ps1
+++ b/Tools/Scripts/check-locales.ps1
@@ -1,7 +1,7 @@
# 多语言词表一致性校验:语言清单、键集合、占位符、格式说明符、未翻译、孤儿键、L.T 引用覆盖
# 以基准语言文件(默认 zh-Hans.yml)为准,同一目录下所有语言必须提供完全相同的键集合
# 语言清单见 locales.json(与 NewLife.Core 对齐),各词表目录的语言文件必须与之完全一致
-# 本仓要求**全部语言全量覆盖**:不使用上游的可选键豁免,任何键缺失或仅回退英文都会失败
+# 本仓要求**全部语言全量覆盖**:不使用上游的可选键豁免,任何键缺失、空译文或回退英文都会失败(仅 MD5 等专有名词白名单例外)
#
# 用法:
# pwsh -File Tools/Scripts/check-locales.ps1 # 本仓默认目录
@@ -21,6 +21,8 @@ $ErrorActionPreference = "Stop"
$root = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
$pattern = '^\s*"((?:[^"\\]|\\.)*)"\s*:\s*"((?:[^"\\]|\\.)*)"\s*$'
$placeholder = '\{(\d+)(?:,-?\d+)?(:[^}]*)?\}'
+# 允许译文与英文原文相同的纯技术标识:MD5 是哈希算法专有名词,不可翻译
+$untranslatedAllowed = @('MD5: {0}')
function Unescape-Yaml([string]$text) {
# 与源生成器保持一致:还原 \\ \" \n \r \t \uXXXX
@@ -184,11 +186,9 @@ foreach ($item in $LocaleDir) {
}
}
- # 未翻译:句子级词条(3 个及以上空白分隔片段,或 2 个片段且以句末标点结尾)的译文不得与英文原文完全相同。
- # 单词型技术词(MD5、Encoder、Handler 等)在部分语言中就是本地通行写法,不在此列。
- $words = @($key -split '\s+' | Where-Object { $_ })
- $isSentence = $words.Count -ge 3 -or ($words.Count -ge 2 -and $key -match '[.!?。!?]$')
- if ($value -ceq $key -and $isSentence) {
+ # 未翻译:除白名单中的专有名词外,任何译文都不得与英文原文完全相同。
+ # 单词型技术词(Encoder、Handler、Failover 等)也必须给出本族语写法。
+ if ($value -ceq $key -and $untranslatedAllowed -notcontains $key) {
$allErrors += "$($file.FullName): 未翻译(译文与英文原文相同)→ $key"
}
}