解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
|
# æ•°æ®åŒ…ç¼–ç 器PacketCodec
> âš ï¸ **æ—§æ ˆæ–‡æ¡£ï¼ˆv12 已退役)**:`PacketCodec`/`PacketFramer` å·²æ•´åˆ ï¼Œæ‹†å¸§èŒè´£ç”± `MessagePump` 承担(è§ã€Šæ¶ˆæ¯åè®®æ ˆã€‹ï¼‰ã€‚æ–‡ä¸â€œç¼“å˜é“¾ + å…±äº«åˆ‡ç‰‡åˆ‡å¸§â€æœºåˆ¶åœ¨æ–°å¸§æ³µä¸å»¶ç»ï¼›`IPacket` æ•°æ®åŒ…本身未å˜ï¼ˆè§ã€Šæ•°æ®åŒ…IPacket》)。
## 概述
`PacketCodec` å¤„ç† TCP 粘包/拆包:把分多次到达的å—节æµï¼Œåˆ‡æˆä¸€å¸§å¸§å®Œæ•´æ•°æ®ã€‚
**模型:å•一缓å˜é“¾ + 共享切片切帧。** æ¯æ¬¡ `Parse` 三æ¥èµ°ï¼š
1. **è¿½åŠ **:本轮数æ®ä»¥å…±äº«åˆ‡ç‰‡ï¼ˆæ‹¥æœ‰å¥æŸ„è¾“å…¥ï¼‰æˆ–è§†å›¾ï¼ˆè§†å›¾è¾“å…¥ï¼‰è¿½åŠ åˆ°ç¼“å˜é“¾å°¾ï¼ˆé›¶æ‹·è´ï¼‰ï¼›
2. **切帧**:调用 `GetFrameLength` 委托(åªè¯»åºåˆ—窗å£ï¼Œå¯è·¨æ®µï¼‰ä»Žç¼“å˜é“¾å¤´è¿žç»åˆ‡å‡ºå®Œæ•´å¸§ï¼ˆå¸§å¤´è·¨èŠ‚ç‚¹ç”±å§”æ‰˜è·¨æ®µæ‹¼è¯»ï¼‰ï¼›
3. **ä¿è‡ªæœ‰**:借阅视图的残片ä¸èƒ½è·¨è½®ï¼Œè½¬ä¸ºè‡ªæœ‰æ‹·è´åŽä¿ç•™ã€‚
**命å空间**:`NewLife.Messaging`
**文档地å€**:https://newlifex.com/core/packet_codec
> 全局缓冲所有æƒä¸Žæ¶ˆæ¯å±‚归属处ç†è§ã€Šç½‘ç»œç¼“å†²æ‰€æœ‰æƒæž¶æž„》。
> WebSocket 链路(æœåŠ¡ç«¯ `Http/WebSocket` ä¸Žä¼šè¯ `Net/Handlers/WebSocketCodec`)的å–帧已切æ¢åˆ°æ•°æ®ç®¡é“ + 帧泵,è§ã€Šæ•°æ®ç®¡é“Pipe》;本编ç 器ä¿ç•™ä¸ºå˜é‡å议路径。2026-09-16 è¯„ä¼°ç»“è®ºï¼šä¿æŒåŒå¼•擎定ä½ã€ä¸åšå•一引擎外观化;帧长接å£ç»Ÿä¸€ä¸ºåºåˆ—版 `GetFrameLength`(旧 IPacket/span åŒå§”æ‰˜å·²åˆ é™¤ï¼‰ï¼Œè§ã€Šç½‘络库架构》。
## 所有æƒè§„则
| 对象 | æ‰€æœ‰æƒ |
|------|--------|
| å…¥å‚ `pk` | 归调用方;`Parse` 从ä¸é‡Šæ”¾ |
| ç¼“å˜æ®‹ç‰‡ | ç¼“å˜æŒæœ‰è‡ªå·±çš„引用:拥有输入→共享切片;视图输入→借用(跨轮残片自动转拷è´ï¼‰ |
| 返回帧 | 共享切片/链(拥有输入:å¯è‡ªç”±æŒæœ‰ï¼Œç”¨åŽ `Dispose`ï¼‰ï¼›è§†å›¾è¾“å…¥æ—¶ä¸ºè§†å›¾ï¼Œä»…æœ¬è½®åŒæ¥æœ‰æ•ˆ |
| `Clear` / `Dispose` | 释放整æ¡ç¼“å˜é“¾æŒæœ‰çš„引用 |
三个零拷è´è¦ç‚¹ï¼š
- **帧æ°å¥½è¦†ç›–æ•´æ¡ç¼“å˜** → 缓å˜èŠ‚ç‚¹ç›´æŽ¥ä½œä¸ºå¸§è½¬ç§»ï¼ˆä¸å†äºŒæ¬¡åˆ†é…);
- **跨节点帧** → `Slice` è‡ªåŠ¨ç»„é“¾ï¼Œæ¯æ®µå„æŒä¸€ä»½å¼•用ã€å„自释放;
- **窗å£å‰ç§»** → 帧与剩余窗å£å„åˆ‡ä¸€ä»½å…±äº«åˆ‡ç‰‡ï¼ˆåŽŸå¥æŸ„å½’è¿˜ï¼‰ï¼Œä¸æ¬ç§»æ•°æ®ã€‚
## äº”ä¸ªæ ¸å¿ƒåœºæ™¯
| # | æœ¬è½®å½¢æ€ | 行为 | 返回帧 | åˆ†é… / æ‹·è´ï¼ˆç»éªŒï¼‰ |
|---|---------|------|--------|--------------------|
| S1 | æ— ç¼“å˜ï¼Œæ°ä¸€å®Œæ•´å¸§ | 缓å˜èŠ‚ç‚¹ç›´æŽ¥ä½œä¸ºå¸§è½¬ç§» | 拥有切片(视图输入ï¼è§†å›¾ï¼‰ | 0 分é…(整帧转移)/ 0 æ‹·è´ |
| S2 | æ— ç¼“å˜ï¼ŒN≥2 个完整帧 | é€å¸§å…±äº«åˆ‡ç‰‡ | æ¯å¸§ä¸€ä»½åˆ‡ç‰‡ | 帧切片 + 剩余窗å£åˆ‡ç‰‡ï¼ˆæœ«å¸§å¥æŸ„转移)/ 0 æ‹·è´ |
| S3 | æ— ç¼“å˜ï¼Œå®Œæ•´å¸§ + 尾残片 | 帧切片;残片留在缓å˜è·¨è½® | 帧切片 | 帧切片 + 剩余窗å£åˆ‡ç‰‡ / 0 æ‹·è´ |
| S4 | 有缓å˜ï¼Œå¹¶å…¥åŽä»ä¸è¶³ä¸€å¸§ | 残片继ç»ç´¯ç§¯ï¼ˆå§”托链扫æ/头部定界) | æ— | è¿½åŠ æ‹¼é“¾é›¶æ‹·è´ï¼›å¸§å¤´è·¨èŠ‚ç‚¹ç”±å§”æ‰˜æ‹¼è¯»ï¼ˆé›¶æ‹·è´ï¼‰ |
| S5 | 有缓å˜ï¼Œå‡‘é½ä¸€å¸§ï¼ˆ+) | 跨节点共享切片组链 | 切片链 | 跨节点组链切片 + 剩余窗å£åˆ‡ç‰‡ / 0 æ‹·è´ |
**典型æµä¸çš„场景分布(ç»éªŒä¼°ç®—,å‡è®¾è¯»çª—å£ 4~8KB)**:
| æµåž‹ | S1 | S2 | S3 | S4 | S5 |
|------|----|----|----|----|----|
| RPC 请求å“应(帧 ≈ 读) | 70~85% | ~0 | 15~30% | <5% | <5% |
| å°å¸§æ‰¹é‡æµæ°´ï¼ˆå¸§ ~100B 级) | ~0 | 多帧轮主导 | æ¯è½® | ≈0 | æ¯è½®ï¼ˆè¡¥æ®‹ç‰‡ï¼‰ |
| 大帧跨轮(帧 ≫ 读窗å£ï¼‰ | 首轮 | ~0 | ~0 | ≥95% | 完æˆè½® |
> 场景å¯ç»„åˆå‡ºçŽ°ï¼ˆå¦‚â€œè¡¥æ®‹ç‰‡ + 多帧 + 新残片â€åŒè½®ï¼‰ï¼›æœ¬è¡¨æ˜¯åŽç»æ–¹æ¡ˆè®¨è®ºä¸Žæ€§èƒ½è¯„估的统一视角。
**边界(å‡å·²è‡ªåЍ处ç†ï¼‰**:帧头跨节点 → 链感知委托跨段拼读(`GetPrefix` ç›´å–å‰ç¼€ / è·¨æ®µæ‹¼å…¥æ ˆç¼“å†²ï¼Œé›¶æ‹·è´ï¼‰ï¼›åˆ†éš”符跨节点 → 链内扫æç›´æŽ¥å‘½ä¸ï¼ˆå«äº¤ç•Œçª—å£ï¼‰ï¼Œé›¶æ‹·è´ï¼›è¶…大帧跨多轮 → 段链零拷è´ç´¯ç§¯ï¼›ç©ºæ•°æ® / è¶…æ—¶ / è¶…ä¸Šé™ â†’ 早退或丢弃。
## 快速开始
```csharp
// æ¯ä¸ªè¿žæŽ¥ç‹¬ç«‹å®žä¾‹ï¼›pk 为本轮收到的数æ®
var parser = new DefaultMessage();
var codec = new PacketCodec
{
GetFrameLength = buffer => parser.TryParse(buffer, out _),
MaxCache = 4 * 1024 * 1024,
};
foreach (var frame in codec.Parse(pk))
{
// 拥有帧(切片/链)å¯è‡ªç”±æŒæœ‰ï¼Œç”¨åŽ Dispose;视图帧仅本轮有效
ProcessFrame(frame);
}
```
## ä¸»è¦æˆå‘˜
| æˆå‘˜ | 说明 |
|------|------|
| `Parse(IPacket pk)` | 唯一入å£ï¼šè¿½åŠ â†’ 切帧 → ä¿è‡ªæœ‰ï¼Œè¿”回完整帧列表 |
| `Clear()` | 清空缓å˜å¹¶é‡Šæ”¾å¼•用,实例å¯ç»§ç»ä½¿ç”¨ |
| `Dispose()` | 释放缓å˜é“¾å¼•ç”¨ï¼›è¿žæŽ¥å…³é—æ—¶è°ƒç”¨ |
| `GetFrameLength` | 帧长解æžå§”托(åªè¯»åºåˆ—窗å£ï¼Œè§ä¸‹èŠ‚ï¼‰ |
| `Last` / `Expire` / `MaxCache` | 时间戳ã€ç¼“å˜è¿‡æœŸä¸Žä¸Šé™ï¼ˆè¾¾åˆ° MaxCache å«ç‰äºŽå³æ•´ä½“丢弃;Expire ≤0 表示残片æ¯è½®ä¸¢å¼ƒï¼‰ |
| `Tracer` | APM 追踪 |
## GetFrameLength 委托
```csharp
public Func<ReadOnlySequence<Byte>, Int32>? GetFrameLength { get; set; }
```
**契约定版**:入å‚为**缓å˜çª—å£ï¼ˆå¸§é¦–èµ·çš„åªè¯»åºåˆ—,å¯è·¨æ®µï¼‰**——帧头跨段由åºåˆ—读å–自然支æŒï¼Œä¸å†éœ€è¦é“¾æ„ŸçŸ¥æ‹¼è¯»ï¼›è¿”回**完整帧长(å¯èƒ½å¤§äºŽçŽ°æœ‰æ•°æ®ï¼‰**;返回 0/è´Ÿå€¼è¡¨ç¤ºæ— æ³•å®šç•Œï¼Œç‰ä¸‹ä¸€è½®ã€‚注æ„çª—å£æ˜¯ç¼“å˜æ€»é‡è€Œéžå¸§é•¿ã€‚
### 常è§å议的 GetFrameLength 实现
```csharp
// NewLife é»˜è®¤æ¶ˆæ¯æ ¼å¼ï¼ˆæ¶ˆæ¯è‡ªè§£æžï¼‰
var parser = new DefaultMessage();
GetFrameLength = buffer => parser.TryParse(buffer, out _);
// 2å—节å°ç«¯é•¿åº¦å—段(Offset+Size,LengthFieldCodec é£Žæ ¼ï¼‰
GetFrameLength = buffer => MessageCodec<DefaultMessage>.GetLength(buffer, 0, 2);
// WebSocket å¸§ï¼ˆå«æ‰©å±•长度/掩ç 头)
var ws = new WebSocketMessage();
GetFrameLength = buffer => ws.TryParse(buffer, out _);
// 分隔符扫æï¼ˆè·¨æ®µæ‰«æï¼Œé›¶æ‹·è´ï¼›å®Œæ•´å®žçŽ°è§ SplitDataCodec.GetLineLength)
GetFrameLength = GetLineLength;
// 固定包长(如 Modbus RTU)
GetFrameLength = buffer => 8;
```
## 与接收层的ååŒ
- 接收层(`SessionBase`)æ¯è½®æŠŠä¸€ä¸ª**æ‹¥æœ‰å¥æŸ„**交给管é“ï¼›
- codec 需è¦ä¿ç•™æ®‹ç‰‡æ—¶ï¼Œåˆ‡ä¸€ä»½å…±äº«å¼•ç”¨æ”¾è¿›ç¼“å˜ â†’ 缓冲引用计数大于 1ï¼›
- 轮末按引用计数è£å†³ï¼š**æ— ä»–äººæŒæœ‰ï¼ˆrc==1)→ 缓冲继ç»å¤ç”¨ï¼Œé›¶ Rent/Returnï¼›æœ‰æŒæœ‰ → æ¢æ–°ç¼“冲**;被带出的缓冲由最åŽçš„æŒæœ‰è€…å½’è¿˜ï¼›
- 帧交给下游åŽï¼šåŒæ¥æ¶ˆè´¹å¯ç›´æŽ¥ä½¿ç”¨ï¼›è·¨è½®/è·¨ await æŒæœ‰æ—¶ç”¨åŽ `Dispose`(事件内带出å¯ç”¨ `Slice` åˆ‡å‡ºå…±äº«å¥æŸ„)。
## 注æ„事项
- **æ¯è¿žæŽ¥ä¸€ä¸ªå®žä¾‹**:`PacketCodec` 有内部状æ€ï¼ˆç¼“å˜é“¾ï¼‰ï¼Œç¦æ¢è·¨è¿žæŽ¥å…±äº«ã€‚
- **`GetLength` 跨段拼读**:帧头å¯è·¨èŠ‚ç‚¹ï¼Œç”¨ `GetPrefix`/`ReadBytes` 拼读定界;分隔符类å议链内扫æï¼›ä¸è¦ä¾èµ– `pk.Total`ï¼ˆé‚£æ˜¯ç¼“å˜æ€»é‡ï¼‰ã€‚
- **`Expire` / `MaxCache`**:防æ¢ç½‘络异常导致残包长期å 用内å˜ã€‚缓å˜é‡è¾¾åˆ° `MaxCache`(å«ç‰äºŽï¼‰æˆ–è·ä¸Šæ¬¡åŠ å…¥æ•°æ®è¶…过 `Expire` æ¯«ç§’æ—¶æ•´ä½“ä¸¢å¼ƒï¼Œå¹¶ç» `Tracer` 错误 Span 上报;æŸå帧(定界返回 0/负值)ç‰å¾…åŽç»æ•°æ®ï¼Œè¿žæŽ¥ä¸ä¸æ–。
- **异常安全**ï¼šå¸§é•¿å§”æ‰˜æˆ–æ®‹ç‰‡æ‹·è´æŠ›å¼‚å¸¸æ—¶ï¼Œå·²åˆ‡å‡ºçš„å¸§å…ˆå½’è¿˜å¼•ç”¨å†æ”¾è¡Œå¼‚å¸¸ï¼›å€Ÿé˜…è¾“å…¥çš„ç¼“å˜æ•´é“¾æ¸…空(视图残片ä¸è·¨è½®ï¼‰ã€‚
- **å…³é—连接时 `Dispose`**:归还缓å˜é“¾å¼•用;内置编ç 器(Standard/LengthField/SplitData/WebSocket)已在 `Close` ä¸è‡ªåŠ¨å½’è¿˜æœ¬ä¼šè¯å®žä¾‹ã€‚
- **会è¯éš”离**:处ç†å™¨å®žä¾‹è¢«å¤šä¼šè¯å…±äº«ï¼Œ`PacketCodec` ç¦æ¢æ”¾å¤„ç†å™¨å—æ®µï¼›åº”å˜ `context.Owner` çš„ `IExtend`(键 `ss["Codec"]`,`WebSocketCodec` 用独立键以å…许级è”)。
- **回归测试**:`XUnitTest.Core/Net/PacketCodecSplitTests.cs`ã€`PacketCodecBigFrameTests.cs`。
|