解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
|
# 网络库同步异步分工
> **结论先行**:v12 网络层不做全面异步化,采用**双轨**——同步直发是默认快路径,异步直发(等待可写期间不占线程)按需使用;排队/限流交给上层 `Actor`。公开同步成员不删除(生态二进制兼容);命名纪律:同步方法不带 `Async` 后缀,返回 `Task` 的方法必须真异步。
>
> 定稿:2026-09-18。破坏性改名迁移对照见 `ChangeLog.md` v12 小节。
## 1. 现状对照表
| 操作 | 同步形态 | 异步形态 | 模式/默认 | 说明 |
|------|----------|----------|-----------|------|
| 接收(事件模式) | — | SAEA 环形接收 + `Received` 事件 | **默认**(`AutoReceive=true`) | 连接打开时自动 `StartReceive()` 启动接收环,数据经事件/处理器链投递 |
| 接收(拉取模式) | `Receive()` | `ReceiveAsync(CT)` | 打开前设 `AutoReceive=false` | 直读原语;启动接收环后拉取抛异常 |
| 字节级发送 | `Send(IPacket / ArraySegment / ReadOnlySpan)` | —(见背压 API) | 默认 | TCP:同步系统调用快路径 + 短计数续发,预算 = `Timeout`;UDP:数据报原子送出 |
| 流式发送 | `Send(Stream)` 扩展(阻塞循环至完成) | `TcpSession.SendAsync(Stream, length, CT)` | 按需 | 读一块写一块,64KB 分块内存有界,内核缓冲即背压 |
| 文件发送 | — | `TcpSession.SendFileAsync(path, CT)` | 按需 | 内核零拷贝推送(sendfile/TransmitFile);SSL 与低版本 TFM 降级为分块发送 |
| 异步直写 | — | `TcpSession.SendAsync(IPacket, CT)` | 按需 | 锁内异步直写,等待可写期间不占线程 |
| 消息级发送 | `SendMessage(Object)` | `SendMessageAsync(Object, CT)` | 双轨 | 请求-响应配对(匹配队列) |
| 连接管理 | `Open()` / `Close(reason)` | `OpenAsync(CT)` / `CloseAsync(reason, CT)` | 双轨 | 同步形态保留供简单场景 |
| WS 发送帧 | `SendText / SendBinary / SendMessage / CloseFrame` | — | 同步形态 | 内部经 WebSocketCodec;帧为小数据,无异步价值 |
| WS 握手 | 静态 `Handshake(client, uri)`(兼容保留) | `OpenAsync` 打开链路内自动完成 | 自动 | 失败释放底层并返回 false |
| 服务端群发 | `SendAllMessage` | `SendAllAsync` | 双轨 | 逐会话送出;`SendAllMessage` 同步直发,`SendAllAsync` 真异步(每会话并行送出、`WhenAll` 收口) |
## 2. 竞品 API 形态对照
| 框架 | 流派 | 连接 | 发送 | 依据 |
|------|------|------|------|------|
| Kestrel / System.IO.Pipelines | 全异步 | — | 无同步发送(Pipe 读写全异步) | 框架契约 |
| DotNetty | 全异步 | `Task` 工厂 | `IChannel.WriteAsync`(唯一写入口) | API 面 |
| SuperSocket v2 | 全异步 | — | `ValueTask SendAsync` | 官方文档 |
| WatsonTcp | 全异步 | — | `Task<Boolean> SendAsync` | API 面 |
| TouchSocket | 双轨 | 双轨 | 同步发送保留(官方明示"三种同步发送方法")+ 异步 | 官方文档 |
| HPSocket(.Net) | 双轨 | 双轨 | 同步/异步并存(C++ 内核队列承接) | 组件矩阵 |
| NetCoreServer | 双轨 | `Connect()`/`ConnectAsync()` 并存 | `SendAsync` 入队即返回 | 源码 |
| BCL `Socket` | 双轨 | `Connect`/`ConnectAsync` | `Send`/`SendAsync` 并存 | 平台基座 |
**规律**:全异步派均为**高层框架**(HTTP 服务器、RPC、消息服务),把异步写死进契约;**字节级底层库(BCL Socket / NetCoreServer / HPSocket / TouchSocket)全部保留同步面**——没有一家砍掉同步发送。本库定位与后者一致。
## 3. 决策记录(为什么不做全面异步化)
1. **接收已全异步**:SAEA 环形接收 + 完成回调,不存在需要改造的"同步接收"。
2. **发送异步化无吞吐收益(本机实测)**:`SendAsync(Stream)` 按 64KB 分块(1MB 回环 492µs)与手工分块、整段发送持平,管道自身开销约 1~2%(见《流式架构性能测试报告》)。
3. **同步直发是微秒级快路径**:小包直发为单次系统调用;全异步化 = 每消息入队 + 调度,延迟不降反升。
4. **生态二进制兼容**:公开同步成员被下游程序集直接引用(CLR 成员引用绑定包含返回类型);删除/改名会断旧二进制(历史教训:三参 `Slice`、`IMessage` 加成员)。v12 允许破坏的仅限"假异步改名"这类语义修正,且生态仓库随升级重编译。
5. **假异步处置原则**:能真异步的(握手)→ 改真异步;不该异步的(群发、WS 小帧发送)→ 去掉 `Async` 后缀诚实命名。返回 `Task` 但同步完成的公开方法不得保留。
## 4. 分工契约(场景 → 推荐 API)
| 场景 | 推荐 | 说明 |
|------|------|------|
| 小包高频请求-响应 | `Send(...)` 直发 | 微秒级,单次系统调用(链式包散列写) |
| 大流量推送 / 慢消费者 | `Send(...)` 直发或 `SendAsync(IPacket, CT)` | 慢对端由内核发送缓冲形成背压;需要排队/限流时在上层用 `Actor` |
| 文件 / 代理等流式数据 | `TcpSession.SendAsync(Stream, ...)` | 64KB 分块,收发两端内存有界 |
| 静态文件 / 大文件零拷贝 | `TcpSession.SendFileAsync(path, CT)` | 内核 sendfile/TransmitFile,应用层不经读块 |
| 事件模式接收 | 默认(`AutoReceive=true`),订阅 `Received` | 打开时自动启动接收环 |
| 拉取模式接收 | `AutoReceive=false` + `Receive()` / `ReceiveAsync(CT)` | 需在打开前设置 |
| 连接管理 | `Open` / `OpenAsync`、`Close` / `CloseAsync` 双轨 | 同步形态保留 |
| WS 发送帧 | `SendText` / `SendBinary` / `CloseFrame` | 同步形态(内部编解码器) |
| WS 握手 | 无需手工(`OpenAsync` 内完成) | 静态 `Handshake` 兜底手工场景 |
| 服务端群发 | `SendAllMessage`(同步)/ `SendAllAsync`(异步) | 逐会话送出;异步形态并行送出后 `WhenAll` 收口 |
## 5. 命名与契约纪律
- 同步方法**不带** `Async` 后缀:`SendAllMessage`、`SendText`、`CloseFrame`、`StartReceive`。
- 返回 `Task`/`ValueTask` 的公开方法**必须真异步**(真正等待 I/O 或事件)。仅契约实现(如 `OnCloseAsync` 返回已完成 Task)例外——那是接口契约的实现细节,不是对外假异步 API。
- 改名一次性完成(v12),迁移对照表见 `ChangeLog.md`。
## 6. 发送架构(默认直发 + 一把写锁,另有可选发送队列,2026-10-01)
### 6.1 直发路径(默认路径)
`TcpSession` 的四个发送入口共用一把写锁(`_writeLock`),任一时刻只有一位写者在写套接字:
| 入口 | 语义 |
|------|------|
| `Send(IPacket)` / `Send(byte[])` / `Send(Span)` | 锁内同步直写,写完才返回;0 分配 0 拷贝(链式包散列写,一次系统调用提交整条链);短计数进入续发循环(多段按段序跳过已发字节),总预算 = `Timeout`,超预算按超时失败(`OnError` + `Close("SendError")`) |
| `SendAsync(IPacket, CT)` | 锁内异步直写,等待可写期间不占线程;链式包逐段写(每段一次 `SendAsync`,通常只有一段)——与同步散列写口径不同,见下注 |
| `SendAsync(Stream, length, CT)` | 读一块 → 写完或挂起 → 再读下一块,内存恒为一块 |
| `SendFileAsync(path, CT)` | 交给内核零拷贝推送(sendfile/TransmitFile),应用层不经读块 |
> **链式帧的两个口径**(2026-10-05 标注):同步 `Send(IPacket)` 对 `Next` 链用散列写(`ToSegments()` 一次系统调用提交整条链),目的是让对端"整帧同窗到达"走零拷贝快路径;异步 `SendAsync(IPacket)`(`NET5_0_OR_GREATER` + 裸套接字)则逐段写出,一条逻辑帧会被拆成多个 TCP 段,对端更可能只先拿到帧头(负载走流式体)。高吞吐的链式帧建议:①改用同步 `Send`;或②先把链并成单段包再异步发。SSL 分支两侧都按流逐个写出(无此差异);低版本 TFM 的异步写降级为同步写完(同样散列写)。
SSL 分支走 `SslStream.Write/WriteAsync`(流内部处理部分写);UDP 为数据报原子发送,不走本路径。
### 6.2 可选发送队列(2026-10-01 重做)
出站曾以 `SendPipe` + `SendPump` 提供**粘性单出口**(2026-09-17 引入):创建后 `Send` 系列全部改入队、由泵批量送出,`FlushAsync` 写侧水位回压,另有 `TrySend` 暂停时拒绝。该形态已删除——粘性单出口会与直发形成两条写入路径,必须再加锁协商才能避免并发写同一 Socket/SslStream;且入队对借用视图与 `byte[]`/`Span` 必须拷贝。
现以 `TcpSession.SendQueue` 提供**可选的第二出口**,`Send` 系列始终直发:
| 项 | 约定 |
|----|------|
| 入口 | 只有异步 `SendQueuedAsync(IPacket, ct)`(所有权转移);无同步重载 |
| 背压 | 出站水位 256K 暂停 / 128K 恢复;积压达上限时入队异步等待,泵写出后自动恢复 |
| 批量 | 泵取走整个读窗口,一次散列写提交多条消息;写出时取同一把写锁(故不会有字节交错) |
| 顺序 | 同一条逻辑消息必须走同一出口;两条出口混用时消息之间不保证先后 |
| 关闭 | 不排空,残余丢弃并归还池缓冲 |
| 线程 | 泵运行在专用线程(LongRunning),不与线程池争抢 |
迁移:`SendPipe.Writer.Append(pk)` + `FlushAsync` → 默认用 `Send(pk)`(同步直发);确需排队/批量时用 `await SendQueuedAsync(pk, ct)`。`TrySend(pk)`(暂停时拒绝)无对应概念,需要“拒绝/丢弃”策略时由上层 `Actor` 决策。详见《数据管道Pipe》出站一节。
### 6.3 异步发送三维评估
| 维度 | 结论 | 依据 |
|------|------|------|
| 吞吐 | 无提升(±5% 内持平) | `SendAsync(Stream)` vs 手工 64KB 分块:1MB 回环 492µs,差距约 1% |
| 延迟 | 直发更优(少入队 + 调度) | 小包直发微秒级;管道是"保险"不是"提速" |
| 线程占用 | 慢对端等待时不占线程 | 现代 TFM 异步直写走真异步 `Socket.SendAsync` + 每段预算 CTS 复用 |
结论:**默认直发**;慢对端场景用异步直发(不占线程);需要排队与限流时在上层用 `Actor` 组合(见《并行模型Actor.md》);批量场景可选走 `SendQueuedAsync`(见 6.2)。
### 6.4 异步直写实现(`NET5_0_OR_GREATER`)
`WriteMemoryAsync` 经 `Socket.SendAsync` 真异步 + 复用 CTS 的每段预算(正常完成解除、取消后重建,预算内取消转 `TimeoutException` 并上报);`net45` ~ `netstandard2.1` / `netcoreapp3.1` 降级为同步写完(语义等价)。失败语义:`<= 0` → `OnError("Send")` + `Close("SendError")`,公开入口返回 -1(`SendFileAsync`、`SendAsync(Stream)` 同)。
### 6.5 背压感知发送 API(2026-09-18 新增,2026-10-01 随队列重做)
原 `SendAsync(IPacket, ct)`(入队 + 水位等待)与 `TrySend(IPacket)`(暂停时拒绝)随粘性单出口一并移除。异步直写语义由同名 `SendAsync(IPacket, ct)` 保留(见 6.1);队列形态的水位等待由 `SendQueuedAsync(IPacket, ct)` 承担(见 6.2);`TrySend` 的“暂停时拒绝”无对应概念,需要时由上层 `Actor` 决定。
## 7. 存量观察项
- `HttpSession.ExecutePipeline`:HTTP 处理契约已异步化(`IHttpHandler.ProcessRequestAsync` 于 2026-10-08 落地,`DelegateHandler` 原生 `await`),但会话级仍是同步处理模型——`HttpSession` 同步等待整条管道(`ExecutePipeline(...).GetAwaiter().GetResult()`)。
- 服务端 `Start()` / `Stop()`:同步语义(绑定 / 监听为快速操作);`NetServer` 群发 `SendAllMessage` 同步直发、`SendAllAsync` 真异步。
- 旧 TFM(`NETFRAMEWORK` / `NETSTANDARD2_0`)WS 握手为同步直读降级(临时收紧 `ReceiveTimeout`);现代 TFM 为带取消令牌的真异步。
- `UdpSession` 发送为独立实现(数据报原子送出);TCP/UDP 同为直发语义,无发送队列。
## 8. 相关文档
- 《网络库架构》§8 文档索引;《网络库竞品分析》§4 API 形态小节
- 《流式架构性能测试报告》(`Doc/性能/`)——分块粒度、管道成本、背压快路径实测
- `ChangeLog.md` v12 小节——破坏性改名迁移对照表
|