diff --git a/CHANGELOG.md b/CHANGELOG.md
index a36417d..b7624a3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
- **Soak 目标矩阵([#66](https://github.com/CSJ608/StreamFrame/issues/66))**:显式选择 Ubuntu net8.0/net10.0、Windows net8.0/net10.0/net48,避免 Ubuntu 启动 .NET Framework 测试;job 名、详细日志与始终尝试上传的 TRX 产物按 OS/TFM 区分,测试断言失败继续使 job 失败。普通 CI 必需检查名不变。
### 变更
+- **发版前文档与注释核对清理**:① 修正 `StreamFrame.Protocols.Xml` 包描述与 DESIGN 中的序列化器旧说法——实现为 XmlReader→XDocument 解码、XmlWriter 编码,从未使用 XmlSerializer;② 删除 `ConnectionMetrics` 关于 netstandard2.0 指标 no-op 兜底的陈旧注释(实际经 `System.Diagnostics.DiagnosticSource` 包提供同款 API,与 csproj/README 一致);③ XML 注释统一全中文——移除 #69 批次遗留的 11 处英文复述行(IStreamConnection/StreamConnectionOptions/IFramer/IStreamingFramer/ICodec),并去掉 `StxEtxFramer` 文档中的内部项目名引用;④ `IFrameDiscardReporting` 措辞修正(该接口由定界器实现,"第三方 codec"→"第三方定界器");⑤ DESIGN 项目结构树补齐现有文件(Diagnostics/Compatibility/RetryDelayScheduler/会话感知类型/bench 与两个 samples),demo 场景数三→五;⑥ CHANGELOG 2.1.0 段去重(新增/变更四处重复条目;经 tag 区间核实 LOGO 随本版本生效为正确说法,错挂的 social preview 句删除——该资产实为 2.2.1 引入)并移除空「暂无」段;⑦ README.en 的 JSON 最简驱动示例从「安装」移至「Codec」小节,与中文版结构对齐。不改变任何代码行为。
- 依赖升级(Dependabot,[PR #60](https://github.com/CSJ608/StreamFrame/pull/60)、[PR #91](https://github.com/CSJ608/StreamFrame/pull/91)、[PR #98](https://github.com/CSJ608/StreamFrame/pull/98),重复的 [#92](https://github.com/CSJ608/StreamFrame/pull/92) 已关闭):① CI Actions——soak 工作流 `actions/checkout` v4→v7、`actions/upload-artifact` v4→v7(全部工作流对齐 Node 24 世代,Node 20 弃用告警清零);② 进包依赖(patch)——`System.IO.Pipelines` / `System.Threading.Channels` / `Microsoft.Bcl.AsyncInterfaces` / `Microsoft.Extensions.Logging.Abstractions` / `System.Diagnostics.DiagnosticSource` 10.0.11→10.0.12;③ 工具链(不进包)——`Meziantou.Analyzer` 3.0.189→3.0.294、`coverlet.collector` 10.0.1→10.1.0、`Microsoft.NET.Test.Sdk` 18.9.0→18.10.1。
- **会话对象化与拆除分段([#68](https://github.com/CSJ608/StreamFrame/issues/68) PR-2)**:会话五元组(socket/Pipe/取消源/接收、解码、发送三任务/编号纪元/建立时刻)收敛为不可变的内部 `Session` 对象——会话任务只操作所属会话的资源(接收循环与发送 worker 绑定本会话 socket,不再读"当前值"字段),直连迁移与拆除竞速中新旧会话互不干扰;拆除只关本会话的 socket(`_socket` 字段降级为诊断/交接镜像)。会话拆除分段:锁内仅快速中止(取消令牌 + **先关 socket 解锁阻塞 I/O**——netfx 上取消令牌无法中止已提交的接收),有界等待(≤2s)移到锁外——高并发重连/停机不再在会话锁上排队,netfx 拆除收敛加快;停机/重连的总阻塞时长与公共 API 的同步/异步行为不变。全套测试(139+139+131)多轮全绿;同机同口径基准抽检(OneWayThroughput vs issue-70 基线):框架路径与 DirectTcp 对照同向漂移(环境噪声),未观测框架特有劣化,每操作分配无系统性增长。
- **状态机与所有权规范([#68](https://github.com/CSJ608/StreamFrame/issues/68) PR-1)**:DESIGN 增补权威清单——状态转换全表与发布段/通知段规则、资源所有权表(会话 socket/监听器/会话五元组/消息通道)、锁与同步原语清单、允许重入点(回调内 Reconnect/Dispose、停机后调用、迟到旧会话任务)及已知限制(拆除同步等待持锁、`_socket` 字段语义——指向后续内部 Session 对象重构);与新增的 `StateOwnershipBaselineTests` 共同构成 #68 会话所有权重构的行为基线。
@@ -108,9 +109,6 @@
- **XML 文档随 NuGet 包发布**(`GenerateDocumentationFile`):此前全量中文 XML 注释从未进包,用户在 IDE 中没有 IntelliSense;现以 `` 补齐全部接口实现成员的文档引用,并由 `TreatWarningsAsErrors` 强制今后不出现未文档化的公共 API。
- 基准测试扩容:`CodecBenchmarks`(XmlDocumentCodec 典型报文编解码开销)与 `EndToEndBenchmarks`(真实 TCP 回环的单向吞吐(双 framer)与往返延迟);基准项目纳入解决方案(IDE 可见,随解决方案构建;`dotnet test` 依 IsTestProject 机制自动跳过)。
-### 修复
-- 暂无
-
## [2.1.0] - 2026-08-27
### 新增
@@ -123,13 +121,6 @@
### 变更
- CI Actions 升级到 Node 24 运行时:`actions/checkout@v7`、`actions/setup-dotnet@v6`、`softprops/action-gh-release@v3`(消除 Node 20 弃用告警)。
- `GetMessages`/发送 worker 改用 `WaitToReadAsync+TryRead` 等价循环(ns2.0 的 Channels 包无 `ReadAllAsync`;各目标行为一致)。
-- 帧定界基准测试 `bench/StreamFrame.Benchmarks`(BenchmarkDotNet):LengthPrefix/StxEtx 的流式 vs 纯函数编码对比与切帧吞吐,为"流式零拷贝"说法提供可复现数据。
-- 项目 LOGO(`docs/logo/`,SVG 母版 + PNG),README 双语版接入展示、两个 NuGet 包启用包图标(随下个版本发布生效)。
-- README 新增 CI 构建状态徽章;main 分支保护(必须走 PR 且 CI 通过、禁 force push/删除、强制线性历史)。
-- 英文版 README(`README.en.md`,与中文版互链),便于国际用户检索与阅读。
-
-### 修复
-- 暂无
## [2.0.0] - 2026-08-27
diff --git a/README.en.md b/README.en.md
index 0105d67..71b22a6 100644
--- a/README.en.md
+++ b/README.en.md
@@ -34,28 +34,6 @@ XML message driver (optional):
dotnet add package StreamFrame.Protocols.Xml
```
-Minimal JSON driver (System.Text.Json, span-written, AOT-safe):
-
-```csharp
-sealed class SystemTextJsonCodec : ICodec
-{
- public static readonly SystemTextJsonCodec Instance = new();
-
- public JsonElement Decode(in ReadOnlySequence frame, CancellationToken ct = default)
- {
- using var document = JsonDocument.Parse(frame.ToArray());
- return document.RootElement.Clone(); // Own data beyond document disposal.
- }
-
- public void Encode(JsonElement message, IBufferWriter writer, CancellationToken ct = default)
- {
- var raw = message.GetRawText(); // serialized form (non-ASCII already escaped)
- var span = writer.GetSpan(Encoding.UTF8.GetMaxByteCount(raw.Length));
- writer.Advance(Encoding.UTF8.GetBytes(raw, span));
- }
-}
-```
-
## Quick start
This is a **single-connection device integration library**. A passive listener serves one client at a time; `AcceptFirstClientOnly = false` keeps the listener open without adding multi-client handling. The snippets below represent separate processes/roles, with a host-provided `ct`. See the production configuration below and dispose connections with `await using`.
@@ -111,7 +89,27 @@ public interface ICodec
}
```
-**Onboarding a new device = writing one driver**: implement `ICodec`, define your message type, pick a framer — everything else is reused. See `StreamFrame.Protocols.Xml` for the official example.
+**Onboarding a new device = writing one driver**: implement `ICodec`, define your message type, pick a framer — everything else is reused. See `StreamFrame.Protocols.Xml` for the official example. A minimal JSON-message driver (System.Text.Json, span-written, AOT-safe):
+
+```csharp
+sealed class SystemTextJsonCodec : ICodec
+{
+ public static readonly SystemTextJsonCodec Instance = new();
+
+ public JsonElement Decode(in ReadOnlySequence frame, CancellationToken ct = default)
+ {
+ using var document = JsonDocument.Parse(frame.ToArray());
+ return document.RootElement.Clone(); // Own data beyond document disposal.
+ }
+
+ public void Encode(JsonElement message, IBufferWriter writer, CancellationToken ct = default)
+ {
+ var raw = message.GetRawText(); // serialized form (non-ASCII already escaped)
+ var span = writer.GetSpan(Encoding.UTF8.GetMaxByteCount(raw.Length));
+ writer.Advance(Encoding.UTF8.GetBytes(raw, span));
+ }
+}
+```
### Connection (`IStreamConnection`) — the transport
diff --git a/docs/DESIGN.md b/docs/DESIGN.md
index 3482d3c..77f52cf 100644
--- a/docs/DESIGN.md
+++ b/docs/DESIGN.md
@@ -11,28 +11,41 @@
```
StreamFrame/
├─ StreamFrame.slnx
-├─ src/StreamFrame/ # 核心库(无业务依赖);所有公共类型都在 StreamFrame 命名空间
-│ ├─ ConnectionState.cs # Connecting / Connected / Retry / Disconnected
-│ ├─ IStreamConnection.cs # IStreamConnection
+├─ src/StreamFrame/ # 核心库(无业务依赖);所有公共类型都在 StreamFrame 命名空间
+│ ├─ ConnectionState.cs # Connecting / Connected / Retry / Disconnected
+│ ├─ IStreamConnection.cs # IStreamConnection
+│ ├─ ISessionAwareStreamConnection.cs # 会话感知收发(可选能力接口)
│ ├─ StreamConnectionOptions.cs
+│ ├─ DecodeErrorPolicy.cs # 帧内容解码失败策略(Disconnect / SkipFrame)
+│ ├─ FrameErrorKind.cs / FrameErrorEventArgs.cs # 帧层诊断事件
+│ ├─ SessionExpiredException.cs # 会话绑定发送失效异常
+│ ├─ SessionMessage.cs # 带会话编号的接收元素
│ ├─ Framing/
-│ │ ├─ IFramer.cs # 帧定界接口(EncodeFrame/TryDecodeFrame/MaxPayloadBytes)
-│ │ ├─ LengthPrefixFramer.cs # 4 字节大端长度头
-│ │ ├─ StxEtxFramer.cs # STX 0x02 … ETX 0x03(plain,不做转义)
-│ │ ├─ IStreamingFramer.cs # 可选:流式单缓冲编码(Begin/EndFrame)
-│ │ ├─ IFrameDiscardReporting.cs # 可选:重同步丢弃字节上报
-│ │ └─ IWrittenBufferWriter.cs # 流式编码用的可回写缓冲抽象
+│ │ ├─ IFramer.cs # 帧定界接口(EncodeFrame/TryDecodeFrame/MaxPayloadBytes)
+│ │ ├─ LengthPrefixFramer.cs # 4 字节大端长度头
+│ │ ├─ StxEtxFramer.cs # STX 0x02 … ETX 0x03(plain,不做转义)
+│ │ ├─ IStreamingFramer.cs # 可选:流式单缓冲编码(Begin/EndFrame)
+│ │ ├─ IFrameDiscardReporting.cs # 可选:重同步丢弃字节上报
+│ │ └─ IWrittenBufferWriter.cs # 流式编码用的可回写缓冲抽象
│ ├─ Codec/
-│ │ ├─ ICodec.cs # ICodec(驱动实现)
-│ │ ├─ BufferWriterStream.cs # IBufferWriter→Stream,供 XmlWriter 直写
-│ │ └─ PooledBufferWriter.cs # 内部:ArrayPool 租用的 IWrittenBufferWriter
-│ └─ Connection/
-│ ├─ StreamConnection.cs # 连接实现(双模式 + 状态机 + 重连 + 生命周期)
-│ ├─ FrameDecoder.cs # 解码循环(切帧 + codec 解码)
-│ └─ SessionFaultException.cs # 会话级故障(内部)
-├─ src/StreamFrame.Protocols.Xml/ # 官方示例驱动:XML 报文
-├─ test/StreamFrame.Tests/ # xUnit 单测
-└─ samples/StreamFrame.Demo/ # 控制台端到端 demo
+│ │ ├─ ICodec.cs # ICodec(驱动实现)
+│ │ ├─ BufferWriterStream.cs # IBufferWriter→Stream,供 XmlWriter 直写
+│ │ └─ PooledBufferWriter.cs # 内部:ArrayPool 租用的 IWrittenBufferWriter
+│ ├─ Connection/
+│ │ ├─ StreamConnection.cs # 连接实现(双模式 + 状态机 + 重连 + 生命周期 + Session 对象)
+│ │ ├─ FrameDecoder.cs # 解码循环(切帧 + codec 解码)
+│ │ ├─ RetryDelayScheduler.cs # 内部:重连退避调度(倍增封顶 + 抖动)
+│ │ └─ SessionFaultException.cs # 会话级故障(内部)
+│ ├─ Diagnostics/
+│ │ └─ ConnectionMetrics.cs # 内部:内置指标(System.Diagnostics.Metrics)
+│ └─ Compatibility/
+│ └─ NetStandard20Shims.cs # netstandard2.0 兼容层(Task.WaitAsync / Socket Memory 重载)
+├─ src/StreamFrame.Protocols.Xml/ # 官方示例驱动:XML 报文
+├─ test/StreamFrame.Tests/ # xUnit 单测
+├─ samples/StreamFrame.Demo/ # 控制台端到端 demo(五场景)
+├─ samples/StreamFrame.Ownership/ # 缓冲所有权示例(源文件纳入测试工程三目标编译)
+├─ samples/StreamFrame.AotSmoke/ # NativeAOT 发布/运行冒烟(CI 非必需检查)
+└─ bench/StreamFrame.Benchmarks/ # BenchmarkDotNet 基准
```
命名空间约定:公共类型统一收敛在根命名空间 `StreamFrame`(2.0 起不再有 `StreamFrame.Abstractions`),用户只需一个 `using StreamFrame;`;目录仅按角色组织文件。
@@ -72,7 +85,7 @@ public interface ICodec
}
```
-驱动=实现 codec + 定义业务消息类。官方示例:`StreamFrame.Protocols.Xml.XmlDocumentCodec`(XmlSerializer 解码 / BufferWriterStream+XmlWriter 编码)。用户按此骨架替换即可支持自定义二进制协议。
+驱动=实现 codec + 定义业务消息类。官方示例:`StreamFrame.Protocols.Xml.XmlDocumentCodec`(XmlReader→XDocument 解码 / BufferWriterStream+XmlWriter 编码)。用户按此骨架替换即可支持自定义二进制协议。
### 3. 连接核心(客户端/服务端双模式)
@@ -209,6 +222,6 @@ socket --ReceiveAsync--> pipe.Writer --[Pipe]--> FrameDecoder.RunAsync
- XML codec:往返、DTD 拒绝(XXE 防御)
- 连接端到端(真实 TCP 回环):断线重连后消息送达、解码失败断线/跳帧、调试钩子抛异常会话存活、接收空闲超时、发送失败重连、未完成帧超限断线、单客户端拒绝
- 易用性与生命周期:等待连接就绪(跨重试/挂起/Dispose 取消)、Start 重入防护、选项构造校验、慢消费者有界接收队列、Start 令牌取消停机(Disconnected 终态/通道关闭)、对端地址语义、双栈监听 IPv4 接入
-- 端到端:`samples/StreamFrame.Demo` 演示 XML+LengthPrefix、文本+STX/ETX、断线重连三场景
+- 端到端:`samples/StreamFrame.Demo` 演示五场景:XML+LengthPrefix、文本+STX/ETX、断线自动重连、心跳保活+接收空闲超时、会话感知收发
运行:`dotnet build StreamFrame.slnx`、`dotnet test`、`dotnet run --project samples/StreamFrame.Demo`
diff --git a/src/StreamFrame.Protocols.Xml/StreamFrame.Protocols.Xml.csproj b/src/StreamFrame.Protocols.Xml/StreamFrame.Protocols.Xml.csproj
index 594dcf0..cdc2ba6 100644
--- a/src/StreamFrame.Protocols.Xml/StreamFrame.Protocols.Xml.csproj
+++ b/src/StreamFrame.Protocols.Xml/StreamFrame.Protocols.Xml.csproj
@@ -33,7 +33,7 @@
StreamFrame.Protocols.Xml
2.6.0
- StreamFrame 的 XML 报文示例驱动:XmlDocumentCodec(XmlSerializer / XmlWriter 编解码)。
+ StreamFrame 的 XML 报文示例驱动:XmlDocumentCodec(XmlReader/XDocument 解码、XmlWriter 编码)。
Marci
MIT
icon.png
diff --git a/src/StreamFrame/Codec/ICodec.cs b/src/StreamFrame/Codec/ICodec.cs
index 01e293f..8d3afb6 100644
--- a/src/StreamFrame/Codec/ICodec.cs
+++ b/src/StreamFrame/Codec/ICodec.cs
@@ -8,12 +8,9 @@ namespace StreamFrame;
/// 由驱动实现——例如 XML 驱动把负载解析为强类型消息,自定义二进制协议驱动手写字节布局。
///
///
-/// 并发 / Concurrency: 同一会话的发送 worker 串行编码,但 Encode 与 Decode 可并行。
+/// 并发:同一会话的发送 worker 串行编码,但 Encode 与 Decode 可并行。
/// 会话拆除只作有界等待,旧任务可能与新会话重叠(包括 Decode/Decode,阻塞的 Encode 也不能假定已退出)。
/// 实现应无共享可变状态或自行同步;跨连接共享实例的调用方负责确认实现支持并发,框架不提供实例级锁。
-/// A send worker serializes its own calls, not the codec instance. Encode/Decode and old/new
-/// session calls can overlap. Use stateless implementations or synchronization; callers sharing
-/// an instance across connections must ensure it is safe. Avoid blocking synchronous methods.
///
/// 业务消息类型,贯穿一条连接。
public interface ICodec
@@ -23,8 +20,6 @@ public interface ICodec
/// frame 仅供同步调用期间借用;返回对象(包括嵌套字段)必须独立拥有数据。
/// 不得未经拷贝保留 frame、其切片或底层 Memory;管线随后推进/释放,异步消费者可能更晚读取。
/// ReadOnlyMemory 只限制写入接口,不转移所有权。本接口不转移管线内存所有权。
- /// Borrow frame only during this synchronous call. Copy retained bytes into message-owned storage;
- /// never return pipeline slices, even as ReadOnlyMemory. No pipeline ownership transfer is provided.
///
TMessage Decode(in ReadOnlySequence frame, CancellationToken ct = default);
@@ -32,8 +27,6 @@ public interface ICodec
///
/// 必须同步完成写入;不得保留 writer 或其缓冲供返回后/异步使用,也不得释放框架拥有的 writer。
/// 调用方应保持已入队消息及其底层数据不变,普通 SendAsync 完成仅表示入队,编码可能尚未开始。
- /// Complete writes synchronously. Do not retain/dispose the writer or retain its buffers after return.
- /// Queued messages must remain unchanged: SendAsync completion does not mean encoding has finished.
///
void Encode(TMessage message, IBufferWriter writer, CancellationToken ct = default);
}
diff --git a/src/StreamFrame/Diagnostics/ConnectionMetrics.cs b/src/StreamFrame/Diagnostics/ConnectionMetrics.cs
index ebc6c34..24ae95f 100644
--- a/src/StreamFrame/Diagnostics/ConnectionMetrics.cs
+++ b/src/StreamFrame/Diagnostics/ConnectionMetrics.cs
@@ -6,7 +6,7 @@ namespace StreamFrame;
/// 连接级内置指标( 名 "StreamFrame",零外部依赖):
/// 帧收发计数、字节收发计数、重连次数、会话时长、发送队列水位。生产部署用
/// MeterListener / OpenTelemetry 订阅 "StreamFrame" 即可观测,不接入则开销为每次记录
-/// 数纳秒级;netstandard2.0 目标为 no-op 兜底(无 Metrics API,记录全部空操作)。
+/// 数纳秒级;netstandard2.0 目标经 System.Diagnostics.DiagnosticSource 包获得同款 API(netfx 运行时可用)。
///
/// 标签:单一 "endpoint"(构造时的 ip:port)——每条连接的地址固定,标签基数受控。
///
diff --git a/src/StreamFrame/Framing/IFrameDiscardReporting.cs b/src/StreamFrame/Framing/IFrameDiscardReporting.cs
index c1ed45a..2c4ec50 100644
--- a/src/StreamFrame/Framing/IFrameDiscardReporting.cs
+++ b/src/StreamFrame/Framing/IFrameDiscardReporting.cs
@@ -11,7 +11,7 @@ namespace StreamFrame;
/// 正常切帧时 discarded 为空。
///
/// 连接层检测到本接口后,会把这些字节通过 FrameError 事件(Kind=DiscardedByResync)
-/// 交给上层调试;不实现本接口的第三方 codec 不受影响,只是没有丢弃上报。
+/// 交给上层调试;不实现本接口的第三方定界器不受影响,只是没有丢弃上报。
///
public interface IFrameDiscardReporting
{
diff --git a/src/StreamFrame/Framing/IFramer.cs b/src/StreamFrame/Framing/IFramer.cs
index 3f705e8..e25fb61 100644
--- a/src/StreamFrame/Framing/IFramer.cs
+++ b/src/StreamFrame/Framing/IFramer.cs
@@ -10,8 +10,6 @@ namespace StreamFrame;
///
/// 同连接编码与切帧可并行,新旧会话也可能重叠;跨连接共享实例前须确认线程安全。
/// 应无共享可变解析状态或自行同步,框架不提供实例级锁。
-/// Encode and decode may overlap, as may old/new sessions. Shared instances must be thread-safe;
-/// keep parsing state local or synchronize it yourself. The framework does not lock the instance.
///
public interface IFramer
{
@@ -21,7 +19,6 @@ public interface IFramer
///
/// 对完整的帧内负载(不含定界字节)加帧定界并写入 。
/// payload、writer 及其缓冲仅供同步调用期间借用,不得留存供异步使用或释放 writer。
- /// Borrow payload/writer only for this call; do not retain buffers or dispose the writer.
///
void EncodeFrame(ReadOnlySpan payload, IBufferWriter writer);
@@ -36,8 +33,6 @@ public interface IFramer
/// 连接层在长度减少时继续解析剩余缓冲,未消费任何字节时等待更多输入。
/// 返回 true 时必须消费完整帧的线上字节(包括空负载帧的定界字节)。
/// payload 可以是输入切片,供连接层紧接着同步 Decode;实现不得留存 buffer/payload。
- /// Payload may borrow the input for the immediately following synchronous codec call;
- /// the framer must not retain either sequence beyond this call.
///
bool TryDecodeFrame(ref ReadOnlySequence buffer, out ReadOnlySequence payload);
}
diff --git a/src/StreamFrame/Framing/IStreamingFramer.cs b/src/StreamFrame/Framing/IStreamingFramer.cs
index 585d721..8ddf664 100644
--- a/src/StreamFrame/Framing/IStreamingFramer.cs
+++ b/src/StreamFrame/Framing/IStreamingFramer.cs
@@ -19,8 +19,6 @@ namespace StreamFrame;
/// 沿用 IFramer 的并发与借用约束。BeginFrame/EndFrame 的配对状态应存于本次 writer,
/// 不应使用实例字段记录当前帧;两个调用之间可穿插解码或其它会话/连接的调用。
/// writer 及缓冲不得被保留供方法返回后使用;EndFrame 使用调用方重新传入的 writer。
-/// IFramer concurrency rules apply. Keep pair state in the supplied writer, not a current-frame field:
-/// other decode/session/connection calls may interleave. Never retain writer/buffers after a method returns.
///
public interface IStreamingFramer : IFramer
{
diff --git a/src/StreamFrame/Framing/StxEtxFramer.cs b/src/StreamFrame/Framing/StxEtxFramer.cs
index 8a48b34..3645266 100644
--- a/src/StreamFrame/Framing/StxEtxFramer.cs
+++ b/src/StreamFrame/Framing/StxEtxFramer.cs
@@ -8,7 +8,7 @@ namespace StreamFrame;
/// 注意:plain 模式要求负载不得包含 0x02/0x03,否则会被误判为帧边界。
/// 适合 XML / 纯文本等已知安全的负载;二进制负载请改用 。
///
-/// 边界处理(与 SamSung 一致):
+/// 边界处理:
///
/// - 在待定帧的 ETX 收到之前若再次遇到 STX,则丢弃当前部分帧并从新 STX 处重新开始。
/// - 无前导 STX 的孤立 ETX 被视为噪声字节跳过。
diff --git a/src/StreamFrame/IStreamConnection.cs b/src/StreamFrame/IStreamConnection.cs
index 4fe6a4a..d48a25e 100644
--- a/src/StreamFrame/IStreamConnection.cs
+++ b/src/StreamFrame/IStreamConnection.cs
@@ -74,7 +74,6 @@ public interface IStreamConnection : IAsyncDisposable
/// 等待连接进入 :已连接时立即完成;否则等到
/// 下一次连接成功、 取消、生命周期取消或连接 Dispose(任务以取消结束)。
/// 终态后的新等待同样以取消结束,即使未传调用方令牌。
- /// Lifetime cancellation/disposal also cancels future waits, even without a caller token.
/// 替代轮询 或 Task.Delay 式等待。
///
Task WaitForConnectedAsync(CancellationToken ct = default);
@@ -83,8 +82,6 @@ public interface IStreamConnection : IAsyncDisposable
///
/// 完成不代表已编码、写入 socket 或远端确认。消息及底层数据应在入队后保持不变。
/// 未出队普通消息可由新会话续发;已出队发送失败的远端结果未知,不保证交付。
- /// Completion means enqueue only, not encoding, socket write or peer acknowledgement.
- /// Keep queued data immutable; queued entries can continue in a new session, but delivery is not guaranteed.
///
Task SendAsync(TMessage message, CancellationToken ct = default);
diff --git a/src/StreamFrame/StreamConnectionOptions.cs b/src/StreamFrame/StreamConnectionOptions.cs
index a5cd5a4..4bde987 100644
--- a/src/StreamFrame/StreamConnectionOptions.cs
+++ b/src/StreamFrame/StreamConnectionOptions.cs
@@ -22,8 +22,7 @@ public sealed class StreamConnectionOptions
/// Socket 接收缓冲区大小(字节)。
public int SocketReceiveBufferSize { get; set; } = 65536;
- /// 发送队列容量(消息数,非字节预算);超过容量时 SendAsync 将等待(背压)。
- /// Capacity counts messages, not bytes; full queues backpressure SendAsync.
+ /// 发送队列容量(消息数,非字节预算);超过容量时 SendAsync 将等待(背压)。
public int SendQueueCapacity { get; set; } = 1024;
/// 编码缓冲区的初始大小(字节)。
@@ -77,7 +76,6 @@ public sealed class StreamConnectionOptions
/// 设为正数后,消费慢时解码循环暂停读取(TCP 背压自然传导到对端),防止慢消费者撑爆内存。
/// 注意:消费完全停滞期间,会话拆除最多等待 2 秒(内部超时)后继续。
/// 容量按消息数而非字节计量;还需预算单条对象大小、处理中消息、等待入队的生产者及管线/socket 缓冲。
- /// Capacity counts messages, not bytes. Budget message size, in-flight work, waiting producers and I/O buffers too.
///
public int ReceiveQueueCapacity { get; set; }