Skip to content

feat: 会话感知收发 ISessionAwareStreamConnection - #42

Merged
CSJ608 merged 3 commits into
mainfrom
feat-session-aware-send
Aug 28, 2026
Merged

feat: 会话感知收发 ISessionAwareStreamConnection#42
CSJ608 merged 3 commits into
mainfrom
feat-session-aware-send

Conversation

@CSJ608

@CSJ608 CSJ608 commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Closes #39

设计讨论评审回执 落地,评审意见全部采纳。

API(新增 ISessionAwareStreamConnection<TMessage> 能力接口,不动 IStreamConnection

  • CurrentSessionId(long):每次真实 TCP 会话建立时分配,单调递增、不复用(有间隔);无会话 = 0;checked 递增,耗尽显式抛出
  • SendInSessionAsync(sessionId, message, ct)整帧写入本机 socket(内核缓冲,不含对端 ACK)后才完成;会话在写出前终止 → SessionExpiredException,绝不转移到新会话重放;远端处理结果视为未知(文档按评审口径收紧为四条公共保证)
  • GetSessionMessages()SessionMessage<T>(消息 + 所属会话编号);与 GetMessages二选一的竞争消费视图(非广播),独立运行中验证等价

评审要点的落法

评审意见 实现
3.1 独立公共编号(不暴露 epoch) _sessionCounter/_currentSessionId,仅真实会话建立时分配
3.2 建立/拆除的线性化 分配先于 Connected 发布(回调/WaitForConnectedAsync 可见时必为有效值);Retry/Disconnected 发布前归零。拆除 fault 只在 Retry 分支与 Shutdown 调用——初版放在 StopSessionCore(StartSession 也会调)会误杀"Connected 已发布、新会话任务未就绪"窗口内注册的条目,net48 线程调度慢时稳定复现;修复后压测覆盖
3.3 注册/拆除竞态 先注册→再复核的双检 + 状态字 CAS(排队中→已认领/已取消/已失效三向互斥);队满、WriteAsync 等待中失效 → 及时结束,迟到入队的旧信封只跳过不发送
3.4 取消的提交点 提交点 = worker 认领(CAS);认领前取消使条目退出,认领后取消无副作用;写入只受会话令牌控制,不传调用方 token 给共享 socket
3.5 不破坏接口实现者 全部新成员在能力接口上
3.6 接收非广播 两处 XML 文档 + 专项测试(同时枚举恰好分流、不重复)

过程中发现的两个测试环境事实(已绕开并记录在测试注释里)

  • Windows 回环对 TCP 背压几乎免疫(SO_RCVBUF=8KB、对端不读,8MB 也能 3ms 全量灌入)——"大帧阻塞"不可依赖,改用编码停顿的测试 Codec 制造确定性时序;
  • net48 线程调度慢,恰好把上面的线性化竞态稳定暴露出来(net8/net10 上几乎不可见)。

测试(12 条新增,真实 TCP 回环)

issue 清单 6 条 + 评审竞态清单 6 条全覆盖;全套件 76×3 = 228 通过。重点:拆除与注册并发压测(15 轮 × 4 发送,成功的每一次对端必收完整帧)、拆除及时失败(含队满等待中的条目,无需等重连)、普通 SendAsync 跨会话续发 vs 绑定发送不重放的并排对照。

其他

  • README 双语新增"会话感知收发(高级)"章节;CHANGELOG Unreleased 记录
  • SendAsync/GetMessages 现有语义一字不改(既有 55×3 用例全绿)

Marci added 3 commits August 28, 2026 19:59
- 新公共 API:CurrentSessionId(每次 TCP 会话分配、单调不复用,无会话=0)、
  SendInSessionAsync(整帧写入本机 socket 才完成;会话失效即 SessionExpiredException
  且绝不跨会话重放)、GetSessionMessages(消息带会话编号的接收视图)、
  SessionExpiredException / SessionMessage;能力接口不动 IStreamConnection(第三方
  实现者不受影响)
- 线性化(评审要点 3.2):编号分配先于 Connected 对外发布(回调/等待器可见时必为
  有效值);Retry/Disconnected 发布前归零;拆除时(Retry 分支/Shutdown)立即 fault
  全部挂起的会话绑定发送,不空等重连——且只在真正的拆除点 fault,StartSession 的
  旧会话清理不 fault(否则误杀"Connected 已发布、任务未就绪"窗口内注册的条目,
  net48 线程调度慢时稳定复现,已修复并有压测覆盖)
- 发送队列内部信封化(状态字 CAS:排队中→已认领/已取消/已失效三向互斥),
  调用方取消的提交点=worker 认领;认领后取消不撕裂帧;普通 SendAsync/GetMessages
  语义一字不改(既有测试全绿)
- 双接收视图明确为二选一的竞争消费者(非广播),XML 文档与 README 说明
- 测试 12 条(真实 TCP 回环):线性化与编号生命周期/整帧写完才完成/拆除及时失败
  (含队满等待中)/会话切换不重放 vs 普通发送仍续发/Dispose 收尾/取消提交点两侧/
  消息带会话编号/视图竞争非广播/独立运行等价/拆除与注册并发压测(成功帧对端必收齐)
- README 双语新增"会话感知收发(高级)"章节
CI(net48) 暴露:被 worker 认领、正在写出的条目与会话拆除竞争时,
整帧恰好在取消生效前写完 → 任务成功,同样符合"整帧写入本机 socket 才完成"
的契约(本地三次都是取消先赢,纯时序)。放宽两处断言:写出中条目不悬挂、
成败皆可(失败时类型仍须合法);排队中条目仍必须失败。
- SessionSwitch 用例补上与另两处一致的断言放宽:被认领、正在写出的条目在
  拆除竞争下允许"整帧恰好写完→成功"的合法结局,不悬挂即可
- RepeatedTimeoutCycles 的轮内 Connected 等待放宽到 8s(与该文件其余等待一致)
- 新增 ModuleInitializer 抬高测试宿主线程池最小线程数:CI 2 核跑机上并行测试的
  Thread.Sleep 停顿阻塞线程池,net48 线程注入慢(约 1 个/500ms),曾拖垮其他
  测试的秒级等待——属环境性抖动,非实现问题
@CSJ608
CSJ608 merged commit af59104 into main Aug 28, 2026
4 checks passed
@CSJ608
CSJ608 deleted the feat-session-aware-send branch August 28, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 增加会话感知的发送确认与消息上下文

1 participant