解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
大石头 authored at 2018-05-15 21:21:05
13.84 KiB
X
# 网络库架构 > 适用范围:NewLife.Core 的 `NewLife.Net`、`NewLife.Messaging`、`NewLife.Data`(数据包部分)与 `NewLife.Http`(服务端)。 > 目标形态:简单架构覆盖 80% 日常场景,剩余 20% 通过明确的扩展点留给下游。 > 配套专题:《数据包IPacket》《数据管道Pipe》《网络缓冲所有权架构》《消息协议栈》《高级二进制序列化》。 ## 1. 分层与数据通路 ``` 应用层 NetServer / NetSession / NetClient ↑ 消息与事件 协议层 IMessageCodec(SrmpCodec / LengthFieldCodec / SplitDataCodec / WebSocketCodec) ↑ 消息(IMessage) 帧泵 MessagePump(管道上定界与交付;头到齐即交付,体流式/内存统一) ↑ 字节流 缓冲层 Pipe(窗口 + 背压) | 每轮拥有句柄(SessionBase 轮末裁决) ↑ 传输层 SessionBase / TcpSession / TcpServer / UdpServer / UdpSession ↑ 接入面 Socket(内置监听与连接) | 外部 Socket 接管(客户端 TcpSession(Socket)) ``` **接收数据流(简)** 1. Socket 读回调进入 `SessionBase.ProcessEvent`:缓冲自借出即包装为拥有句柄(`OwnerPacket`)挂在接收槽,轮末按引用计数裁决——`RefCount==1` 时句柄回挂接收槽、下一轮 `Resize` 重设窗口复用(缓冲原地不动),否则解绑换新(零 Rent/Return 快路径;归还只发生在释放槽句柄处)。 2. 连接型会话(`TcpSession`)把本轮数据追加进数据管道(`Pipe`,有界背压);UDP 数据报即完整帧,直接逐条分发。 3. 定界:协议模式由 `MessagePump` 从管道定界交付(头到齐即交付,体为内存视图或流式);UDP 一跳多帧无粘包处理。 4. 消息回流到会话:`Received` 事件 → `NetSession` → 可选的 `INetHandler`(如 `HttpSession`)→ `NetServer.Received`。 **接收模式(二选一)**:打开前设置 `AutoReceive`(`SessionBase`,默认 true)——true 自动启动接收环走事件推送(接收环运行期间 `Receive/ReceiveAsync` 抛出异常);false 只允许拉取直读。两模式互斥;拉取模式下可调用无参 `ReceiveAsync()` 手动转事件模式(单向,不回退)。WebSocket 握手发生在打开过程的接收环启动之前,直读天然合法。 **发送数据流**:`Send` → 协议构建整帧(`Build`/`BuildHeader` + 流式体)→ Socket(或 SSL `_Stream`)→ 网卡。 ## 2. 80% 黄金用法 ### 2.1 服务端 + 编解码 ```csharp class MyServer : NetServer<MySession> { } class MySession : NetSession<MyServer> { protected override void OnReceive(ReceivedEventArgs e) { var pk = e.Packet; // 当前指令的数据帧 if (pk == null || pk.Length == 0) return; Send(pk); // 回发 } } var server = new MyServer { Port = 0 }; server.Protocol = new SrmpCodec(); // 或 LengthFieldCodec / SplitDataCodec / CompressedCodec / 自定义 server.Start(); ``` - 内置协议:`SrmpCodec`(新生命标准封包)、`LengthFieldCodec`(长度字段,MQTT 风格)、`SplitDataCodec`(分隔符)、`CompressedCodec`(压缩装饰器,可组合)。 - 也可以不定义会话子类,直接 `server.Received += ...` 集中处理。 ### 2.2 客户端 ```csharp var client = new NetUri("tcp://127.0.0.1:1234").CreateRemote(); client.Protocol = new SrmpCodec(); client.Open(); client.Send("Hello".GetBytes()); ``` - 请求-响应:`SendMessageAsync`,由会话匹配队列(按协议 `IMessageMatcher` 配对)完成应答配对;匹配是**交付阶段**能力,流式与数据报会话同口径(TCP 在消息泵交付时配对,UDP 在数据报交付时配对)。 - 断线重连等应用级能力可使用 `NetClient`。 - 纯拉取客户端(Modbus/短连接请求-响应风格):打开前设 `client.AutoReceive = false`,用 `Receive()/ReceiveAsync(ct)` 直读 Socket。 ### 2.3 自定义协议 - 实现 `IMessageCodec`(`TryParse` 定界构造 / `Build` 整帧 / `BuildHeader` 头部);参考 `Messaging/SrmpCodec.cs` 与 `LengthFieldCodec.cs`。 - 协议实例须无状态(可跨连接共享);可经 `CompressedCodec` 等装饰器组合变换层(压缩/加密),无需多层管道。 ### 2.4 流式协议(新协议推荐) - 连接型会话(`TcpSession`,接口 `IStreamSession`)的 `Pipe` 懒建:`Pipe` 承载字节流窗口与背压,`MessagePump` 定界交付消息(头到齐即交付,体流式/内存统一,跨轮等待)。 - 出站方向:**默认直发 + 一把写锁**(四个入口共用)——`Send(IPacket/byte[]/Span)` 锁内同步直写(写完才返回,0 分配 0 拷贝,链式包散列写一次系统调用)、`SendAsync(IPacket)` 锁内异步直写(等待可写不占线程)、`SendAsync(Stream)` 分块读写(内存恒为一块,内核缓冲即背压)、 `SendFileAsync(path)` 交给内核零拷贝推文件(sendfile/TransmitFile;SSL 与低版本 TFM 降级为分块发送);慢对端由内核发送缓冲形成背压,需要排队/限流时在上层用 `Actor` 组合;批量场景可选走 `TcpSession.SendQueue`(可选第二出口:只异步 `SendQueuedAsync`、所有权转移、积压等待、泵批量写出并取同一把写锁、关闭不排空,见《数据管道Pipe》出站一节);消息协议头 + 流式体:`codec.BuildHeader(msg, length)` 声明长度后接 `SendAsync(Stream)`。 - 传输层大负载:HTTP 响应体 `HttpResponse.BodyStream`(可寻址流→Content-Length,未知长度→`Transfer-Encoding: chunked`;静态文件/嵌入资源已接入,大文件不物化);WebSocket 大帧经 `WebSocketCodec` 构建。 - 细节与示例见《数据管道Pipe》。 ### 2.5 消息契约(IMessage / IMessageCodec) - 消息契约 `IMessage`:`MessageKinds Kind` 四态(与 SRMP 头高 2 位对应,`Reply`/`OneWay` 便捷视图)、`Payload`(消息体句柄视图,流式为 null)、`Body`(内存视图或限长流式读取器)、`SetBody`/`BindBody`、`CreateReply`(应答上返回 null)、`Dispose`。 - 协议契约 `IMessageCodec`:`TryParse`(定界并构造消息,头部不足返回 null)、`Build`(整帧构建,转移语义:构建后消息不再持有体)、`BuildHeader`(头 + 流式体);无状态可跨连接共享。请求-响应配对经 `IMessageMatcher`(如 SRMP 按序列号)。 - 帧定界/构建完全由协议对象承担,消息不感知帧字节;可组合装饰器(如 `CompressedCodec`)实现变换层。并行处理见 `MaxConcurrency`。详见《消息协议栈》。 ## 3. 20% 扩展点目录 | 深度 | 扩展方式 | 关键类型 | |:----:|----------|----------| | 1 | 自定义协议 | 实现 `IMessageCodec`;`server.Protocol = ...` 设置 | | 2 | 自定义会话 | `NetServer<TSession>` / `NetSession<TServer>`;override `OnConnected / OnDisconnected / OnReceive` | | 3 | 会话级业务处理器 | `INetHandler`(`NetServer.CreateHandler` 返回,如 HttpServer → HttpSession) | | 4 | 请求响应匹配策略 | `IMatchQueue`(`DefaultMatchQueue.Add / Match` 可重载) | | 5 | 接管外部 Socket | 客户端 `new TcpSession(socket)` + `Open()`(跳过连接直接收发);服务端暂无接管入口,对外部服务器对象用 `NetServer.AttachServer(ISocketServer)` 挂接(见下行) | | 6 | 自定义传输 | `ITransport`(帧传输抽象,串口/自定义链路接入);`NetServer.AttachServer(ISocketServer)` 挂接自定义服务器 | | 7 | 数据管道回调 | `TcpSession.CreatePipe` 重载(`IStreamSession`);`MessagePump.ReadAsync` | ## 4. 交付上下文契约(数据可带上传输对象) - 协议层/处理器:事件参数(`ReceivedEventArgs`,裸模式含 `INetHandler` 旁路)可回溯到会话对象(`ISocketRemote`);裸 Socket 经 `ISocket.Client` 获取。 - 应用层:`NetSession.Session`(`ISocketSession`)→ `ISocket.Client` 获取底层 Socket;`Remote/Local` 等地址信息同源。 - 接管场景:`TcpSession(Socket)` 接管后,会话的 `Client` 即为调用方传入的 Socket;客户端接管会话的 `ISocketSession.Server` 为空。 - 原则:不新增平行的数据上下文对象;所有投递入口(`Received` 事件、`INetHandler`、流式消息体)都在会话作用域内执行,可回溯到会话与传输对象。 ## 5. 帧引擎 帧定界与构建由单一帧泵 `MessagePump` 承担:管道上定界交付(头到齐即交付、小帧零拷贝快路径、大帧流式体、`MaxCache` 残余防护),协议经无状态 `IMessageCodec` 实现。 ## 6. 兼容纪律(改动本库前必读) - 公开 API 的删除、改名、返回类型收窄都会断开旧二进制(CLR 成员引用绑定包含返回类型);优先新增成员。 - 兼容壳的处置不同:`OwnerPacket.Free` 壳已删除;三参 `Slice` 壳(`[Obsolete]`)**保留**(二进制兼容面),语义已统一为引用计数共享(`transferOwner` 被忽略)——旧二进制可继续加载,但"只释放新句柄"的旧用法会让原句柄引用滞留(延迟到 GC,非错误);源码升级请改用两参 `Slice`。 - 变更公开面后的默认门禁:全 18 TFM 编译(含 net45)+ 全量测试 + 生态扫描(`C:\X` 下游仓库与 NuGet 缓存 `newlife.*` 产物)。 - 零调用成员清理(2026-10):本库范围内部成员直接删除(如 `TcpSession.DirectSendAsync(ReadOnlyMemory)`、`XmlHelper` 标量换算簇);公开面删除项(`EventHub.EncodeEvent/EncodeAction`、`HttpHelper.MakeRequest`/`PostMultipartFormAsync`/`ConsumeAndPushAsync`/`WaitForClose` 等)见《消息协议栈》§8 v12 清理清单,均经 `C:\X` 全生态零引用复核;保留项与误报类别见《架构设计》§3。 ## 7. 文档索引 - 数据与帧:《数据包IPacket》《数据管道Pipe》《数据包编码器PacketCodec》《网络缓冲所有权架构》《高级二进制序列化》 - 同步/异步:《网络库同步异步分工》(双轨决策、场景选型、发送架构:直发 + 一把写锁)、《会话关闭与在途帧交付》(关闭前交付在途帧、取消不丢已到达数据) - 标准封包:`NewLife.Core/Net/Readme.md`(SRMP) - 用法样例:`Samples/Zero.EchoServer`、`Samples/Zero.Server`;基准:`Benchmark/NetBenchmarks`、`Benchmark/PacketBenchmarks` ## 8. 会话生命周期状态权威 > 现状:同一个会话“还在不在/活没活”同时记在六处——`TcpServer._Sessions`、`UdpServer._Sessions`、`NetServer._Sessions`、`SessionBase._active`、`NetSession._running`、`SessionCollection`。任一处判断失败都会留下“集合里有、接收没起、Disposed 未置”的半死态。本节把这六处定死为“一个终态 + 一个存在性 + 若干派生量”,以文档 + XML 注释 + 契约用例固化;不合并结构的取舍见《架构设计》§3 关键设计决策。 ### 8.1 权威与派生 | 状态 | 位置 | 定位 | |---|---|---| | `Disposed` | `DisposeBase.Dispose`(`Common/DisposeBase.cs`) | **终态唯一权威**。一次性置位,`OnDisposed` 是所有集合摘除的统一回落点;判断“结束了没有”只认它 | | `ISocketServer.Sessions`(`SessionCollection._dic`) | `Net/SessionCollection.cs` | **Socket 层存在性权威**。只收未释放会话(`Add` 入集合前二次复查 `Disposed`);出集合只有三条路:`OnDisposed` 回调、`RemoveNotAlive` 超时清理、`CloseAll`/`Clear` | | `NetServer.Sessions` | `NetServer.cs` | **应用层第二份登记**(`INetSession` 包装器),受 `UseSession` 开关控制,与上一行不是一一对应;`UseSession=false` 时该集合恒空 | | `SessionBase.Active` | `Net/SessionBase.cs` | “传输可用”,**不代表**“接收环已启动”,也**不代表**“在集合中”。客户端会话打开成功置 true、关闭完成置 false;服务端会话由宿主在构造时即置 true(表示“已接受连接”)、关闭起始处提前置 false(阻断掉线重连),因此服务端会话的 `Open()` 会幂等短路,`OnOpenAsync` 根本不会执行 | | `NetSession._running` | `NetSession.cs` | 应用层一次性闸门:“`Start` 已生效且 `Close` 未发生”,用于保证 `OnConnected`/`OnDisconnected` 各一次;与 `Active`/`Disposed` 无联动 | | 派生量(不作权威判定) | `SessionCollection._endPoints`、`UdpServer._broadcasts`、`NetServer._SessionCount`/`SessionCount`/`MaxSessionCount`、`NetServer.Active`、`NetClient.Active`、`TcpServer.Active` | 缓存与观测。`_endPoints`/`_broadcasts` 是与集合同源同销(同一 `OnDisposed`)的索引;`NetServer.Active`/`NetClient.Active` 由内部状态算出 | ### 8.2 不变量(由契约用例锁定) 1. 会话集合只收未释放会话;**释放 ⇒ 必出集合**。 2. 启动失败或业务回调抛异常时,会话必须收敛为 `Disposed` 并出集合(用例见 `TcpServerAcceptRollbackTests`、`UdpServerCreateSessionRollbackTests`)。 3. `Active=false && !Disposed && 在集合` 是**允许的瞬态**,但**必须收敛**:服务端会话关闭后必须 `Disposed` 并出集合;客户端会话关闭后保持 `!Disposed`,由调用方保管终态——会话对象一次性,关闭后不可重新打开,重连请新建对象(见 `TcpSessionSingleUseTests`)。 4. 服务端会话的 `Open()` 幂等成功(连接已由宿主接管),`Close()` 走正常关闭流程。 5. UDP 差异:`UdpSession` 没有 `Active`,存活表达就是 `!Disposed`;`Stop()` 后 `Server=null` 表示“与服务器脱钩”而非“已死”,集合查找只认 `Disposed`;会话级拉取明确拒绝(`Receive/ReceiveAsync` 抛 `NotSupportedException`,拉取统一归 `UdpServer`,见《架构设计》§3 设计决策,用例见 `PullModeTests`)。 6. 计数口径:`SessionCount` 统计服务端存活会话(与 `UseSession` 无关),`Sessions.Count` 是已登记的包装器数;两者在 `UseSession=true` 且稳态下相等。