docs(net): N-32 新增 §10 监听生命周期模块化边界设计(未执行)
用户诉求「高内聚低耦合」的落地方案:把监听生命周期(relisten 全套 + pump + 排空 + 隔离期)从 4126 行的 net_socket.c 抽成 net_listener.c (真机专属),net_socket.c 回归「数据通道」本职。 本次**精确清点**得出的关键数据: - 搬移量 ≈470 行(函数块 414 + 常量/状态 55) - 跨边界引用面**很小**:状态数组外部写仅 2 处、try 4 处、clear 2 处、 tick 2 处 ⇒ **接口只需 4 个函数 + 1 个枚举**,无需暴露任何数组 - 新模块用 p_sock->ch395_sock 替代反推 fd ⇒ 完全不需访问 s_net_socks 边界逐条给了理由:搬 7 函数 + 7 状态数组 + 8 常量;留 net_listen_locked (应用 API 路径)/ net_accept_locked(产出连接)/ net_lost_connect_probe (补 accept)/ net_listener_armed(双端公开 API)/ net_resync_listen / net_reconcile_tick / net_sock_t 字段。 **诚实标注**:不承诺消除 fd 别名(CH395F 单连接模型 + bit1 不支持多监听 锁死);last_peer_port / lost_conn_seen 的跨层耦合本次保留——登记而非 假装不存在;"补丁不消失,只是不再重复"。 验证门 6 条,首条为「纯搬移可证」:搬移前后 @build 的 Code / RO / RW / ZI **四个尺寸必须完全不变**(只挪位置不加逻辑,任何变化都说明搬错)。 执行顺序 4 步,前两步纯组织变更可只跑 @build + @test,第 3 步后才上完整 台架矩阵。
This commit is contained in:
@@ -354,3 +354,148 @@ RAM 预算台账 `.md`,属另一在途任务)。这两个文件在主工作
|
||||
> (最近一次)为 10999 µs 即**当前正常**,判为一次性卡顿,**留待观察**。
|
||||
|
||||
**回滚点**:P0 纯注释可直接 revert;P1 若回归失败 revert 单提交即可(④ 仍在,退回到"端口活着但慢消费者可能卡死"的已知状态);P2a 独立成提交(`7cb5af0`),revert 后回到"对账③④ 兜底 + 12 所有者"的 P1 状态;P2b 未做。
|
||||
|
||||
---
|
||||
|
||||
## 10. 监听生命周期模块化边界设计(重构方案,**未执行**)
|
||||
|
||||
> **动机**:真机 net 层 `net_socket.c` 4126 行,而模拟器做同样的事只有 807 行(5 倍);
|
||||
> `relisten` 一词在文件里出现 110 次。用户诉求 = **高内聚低耦合**。
|
||||
> P2a 把「周期性重建」收敛到 `net_listener_pump`(所有者 7→5)之后,搬移边界才**第一次清晰**——
|
||||
> 这正是 P2a 相对"直接抽模块"的价值:先收敛,再搬移。
|
||||
>
|
||||
> 本节只定**边界与接口**,不含任何代码;执行须过 §10.6 的验证门。
|
||||
|
||||
### 10.1 目标与可验证判据
|
||||
|
||||
「高内聚」在本项目落成**三条可机械检验**的判据(不是感觉):
|
||||
|
||||
| # | 判据 | 现状 | 目标 |
|
||||
|---|---|---|---|
|
||||
| 1 | 模块外调用「重建监听」的**入口数** | 5(pump / 显式消息 / recv / accept / pending) | **1**(新模块导出) |
|
||||
| 2 | `s_relisten_*` 静态状态被**模块外直接读写** | 2 处外部写 | **0** |
|
||||
| 3 | 依赖方向 | 无 | 新模块 → `net_socket.c` **单向**,无反向 include |
|
||||
|
||||
⚠ **明确不承诺**:本重构**不消除 fd 别名**(同一 Socket 既当监听器又当连接)。
|
||||
那是 CH395F 单连接模型 + bit1 多连接模式**不支持多监听**(Trap 记录改造清单第 4 条)锁死的,
|
||||
属硬件上限(§3 D1)。重构只提升**组织度**,不改变架构上限——**别指望补丁消失,只指望补丁不再重复**。
|
||||
|
||||
### 10.2 现状清点(2026-10-12,`net_socket.c` 4126 行)
|
||||
|
||||
| 类别 | 数量 | 明细 |
|
||||
|---|---|---|
|
||||
| 纯监听生命周期函数 | **7** | `net_relisten_locked`(139) / `net_listener_pump`(107) / `net_relisten_retry_tick`(47) / `net_relisten_try`(45) / `net_drain_chip_rx`(32) / `net_relisten_clear`(28) / `net_relisten_done`(16) |
|
||||
| 静态状态数组 | **7** | `s_relisten_quarantine` / `_settle_start` / `_settle_dead` / `_wait` / `_next` / `_dead` / `_mark` |
|
||||
| 常量 | **8** | `NET_RELISTEN_{QUARANTINE,GRACEFUL,SETTLE}_MS` / `NET_RELISTEN_SETTLE_{QUARANTINE,GRACEFUL}` / `NET_RELISTEN_{RETRY,DEADLINE}_MS` / `NET_LISTENER_PUMP_PERIOD_MS` |
|
||||
| **搬移量小计** | **≈470 行** | 函数块 414 + 常量/状态注释块 55 |
|
||||
|
||||
**跨边界引用点**(这个数字决定接口面大小,是本设计的核心依据):
|
||||
|
||||
| 引用 | 处数 | 位置 |
|
||||
|---|---|---|
|
||||
| `s_relisten_quarantine[fd] = <level>` 外部**写** | **2** | `handle_disconnect_event`(GRACEFUL)、`handle_timeout_event`(QUARANTINE) |
|
||||
| `net_relisten_try()` 外部调用 | **4** | `NET_MSG_RELISTEN` 处理、`net_recv_locked`、`net_accept_locked`、`net_pending_tick` |
|
||||
| `net_relisten_clear()` 外部调用 | **2** | `net_socket`(新 fd)、`net_close_locked` |
|
||||
| 自愈 tick 调用 | **2** | `net_process_messages`(`retry_tick` + `pump`) |
|
||||
|
||||
> **关键观察**:状态数组的**外部写只有 2 处**、且都是「登记沉降等级」这一个语义。
|
||||
> 这说明模块边界天然干净——**接口面只需 4 个函数**,不需要暴露任何数组。
|
||||
|
||||
### 10.3 边界划分
|
||||
|
||||
**搬入新模块** `Drivers/BSP/NET/net_listener.c`(**真机专属**,sim 不编):
|
||||
|
||||
| 搬入 | 理由 |
|
||||
|---|---|
|
||||
| `net_relisten_locked` / `net_relisten_try` / `net_relisten_retry_tick` / `net_relisten_clear` / `net_relisten_done` | 「维持 fd 处于 LISTEN 态」的全部机制 |
|
||||
| `net_listener_pump` | 唯一的周期性重建决策者(P2a 成果) |
|
||||
| `net_drain_chip_rx` | 重建前的排空(只被 relisten 调用) |
|
||||
| 7 个 `s_relisten_*` + 8 个 `NET_RELISTEN_*` / `PUMP_PERIOD` 常量 | 状态与策略私有化 |
|
||||
|
||||
**留在 `net_socket.c`**(并说明理由,避免"看着像就搬"):
|
||||
|
||||
| 留下 | 理由 |
|
||||
|---|---|
|
||||
| `net_listen_locked` | 是**应用 API 路径**(首次监听,含 §9 待议的忙等 P2b),且要处理 BOUND/CREATED 状态门与缓冲——属"连接层建监听",不是"维持监听" |
|
||||
| `net_accept_locked` | 产出的是**连接**(LISTENING→ESTABLISHED 迁移),属连接层;它只**调用**新模块 |
|
||||
| `net_lost_connect_probe` | 做的是"补 accept"(连接建立),不是重建监听 |
|
||||
| `net_listener_armed` | **双端公开 API**(sim 也实现);纯 RAM 查询,很薄,留在原处避免 sim 缺符号 |
|
||||
| `net_resync_listen` | 公开 API + 消息分派;它**只改本层状态**,本就不调 relisten |
|
||||
| `net_reconcile_tick` | ①② 是**连接**失同步自愈(③④ 已在 P2a 移交 pump) |
|
||||
| `net_sock_t` 全部字段 | 数据归 `net_types.h`(双端共享核心),**不随模块走** |
|
||||
|
||||
> ⚠ `net_sock_t` 的 `last_peer_port` / `lost_conn_seen` 是**跨层字段**:新模块的
|
||||
> `net_relisten_locked` 要写它们(转登幽灵甄别基准),`net_lost_connect_probe` 与对账②
|
||||
> 也要读写。**这是本次重构无法消除的残余耦合**(单连接模型的固有结果),
|
||||
> 明确登记而非假装不存在。
|
||||
|
||||
### 10.4 接口设计(4 函数 + 1 枚举,全部窄接口)
|
||||
|
||||
```c
|
||||
/* net_listener.h —— 只被 net_socket.c include(内部头,不进公开 net_socket.h,故 sim 无依赖) */
|
||||
|
||||
/* 沉降/隔离等级(替代外部直接写 s_relisten_quarantine) */
|
||||
typedef enum {
|
||||
NET_LISTENER_SETTLE_GRACEFUL = 2, /* 正常释放(DISCONNECT):等芯片收尾 100ms */
|
||||
NET_LISTENER_SETTLE_QUARANTINE = 1, /* 异常释放(TIMEOUT/RST):等在途落尽 + 排空残渣 400ms */
|
||||
} net_listener_settle_t;
|
||||
|
||||
/* 登记「本 fd 的会话已结束,重建前需沉降」—— 由 handle_disconnect/timeout_event 调用 */
|
||||
void net_listener_settle(int sockfd, net_listener_settle_t level);
|
||||
|
||||
/* 尝试重建一次(不阻塞);已在重试队列则返回 -1 —— 替代 4 处 net_relisten_try */
|
||||
int net_listener_try(net_sock_t *p_sock);
|
||||
|
||||
/* 清本 fd 的重建/沉降状态(新 fd 或 close 时)—— 替代 2 处 net_relisten_clear */
|
||||
void net_listener_clear(int sockfd);
|
||||
|
||||
/* 自愈节拍:D10 重试轮询 + 监听器 pump(合二为一,调用方只记一个入口) */
|
||||
void net_listener_tick(void);
|
||||
```
|
||||
|
||||
**反向依赖(新模块需要 `net_socket.c` 提供的)**:
|
||||
- `net_sock_t` 定义 + 字段(`net_types.h`,共享头,**不算反向依赖**)
|
||||
- `s_net_socks` 数组 —— **问题点**:它是 `net_socket.c` 的 static。
|
||||
**解法**:新模块**不反推 fd**,一律用 `p_sock->ch395_sock`(值等于 fd,`net_socket` 初始化时保证)
|
||||
或调用方传入的 `sockfd`。这样**完全不需要访问 `s_net_socks`**,依赖面进一步收窄。
|
||||
- `net_set_errno` / `NET_ERR_RETURN_1`(错误码)
|
||||
- `ch395f_*`(驱动,本来就是外部依赖)
|
||||
- `HAL_GetTick` / `lat_probe_*`(TEST_LATENCY_PROBE 构建)
|
||||
|
||||
### 10.5 构建与注册
|
||||
|
||||
| 项 | 动作 |
|
||||
|---|---|
|
||||
| `MDK-ARM/STM32F407-Demo.uvprojx` | 加 `<File>` 条目;`Drivers/BSP/NET` 已在 IncludePath(同目录,**无需新增路径**) |
|
||||
| `simulator/CMakeLists.txt` | **不动** —— 新模块真机专属;sim 用 `sim/net_socket_sim.c` 自洽(无孤儿态、无 relisten) |
|
||||
| 注册校验 | 跑 `registration-check`(双端注册反向校验)确认没有单边注册 |
|
||||
|
||||
> ⚠ 按项目约定,新 `.c` 文件的注册走 **`new-source-file` skill**(uvprojx + CMakeLists 双端),
|
||||
> 不要手工编辑工程文件。
|
||||
|
||||
### 10.6 验证门(执行时必须逐条过)
|
||||
|
||||
1. **纯搬移可证**:搬移前后 `@build` 的 `Code`/`RO-data`/`RW-data`/`ZI-data` **完全不变**
|
||||
(只挪位置、不加逻辑;任何数字变化都说明搬错了)。
|
||||
2. `@test` PASS + 回读 `No difference found`。
|
||||
3. 全量回归矩阵:`:21` 0ms×50 / 100ms×50、`:8888` 0ms×20、`:7003` 300ms×10、
|
||||
`ftp_regression` 11/11、Trap23 abort/graceful 各 5/5。
|
||||
4. 设备侧 `orphan listener` 增量 **0**(P2a 已达成的基线,不得回退)。
|
||||
5. `net_listener.c` **不 include** `net_socket.c` 的任何符号(除 `net_types.h`)——
|
||||
用 `grep -n '#include' net_listener.c` 人工核对。
|
||||
6. 判据 §10.1 三条**逐条量化复核**(入口数 / 外部写 / 依赖方向)。
|
||||
|
||||
### 10.7 明确不做(边界外,避免范围失控)
|
||||
|
||||
- **不动** `net_listen_locked` 的忙等(那是 P2b,已判暂缓)。
|
||||
- **不动** `net_sock_t` 字段布局(`last_peer_port` / `lost_conn_seen` 的跨层耦合本次保留)。
|
||||
- **不动** sim 后端(D6:双模型分裂暂不消除;P1 已让**应用层契约**对齐)。
|
||||
- **不做** P3 形态 A(listener 一等对象 + accept 返回虚拟句柄)—— 硬件不支持多监听,已出局(D1)。
|
||||
- **不引入**第二套并行机制(沿用「同一能力只有一个实现」的收敛原则)。
|
||||
|
||||
### 10.8 执行顺序建议
|
||||
|
||||
1. **先搬状态与常量**(7 数组 + 8 常量)→ 编译验证(此时功能等价,只换位置);
|
||||
2. **再搬 7 个函数**,同步把 8 处外部引用改成 4 个窄接口;
|
||||
3. **最后合并 tick**(`net_listener_tick` 内部调 retry + pump),`net_process_messages` 少一个调用点;
|
||||
4. 每步独立提交、独立 `@build`;**第 3 步后**才跑完整台架矩阵(前两步是纯组织变更,可只跑 `@build` + `@test`)。
|
||||
|
||||
Reference in New Issue
Block a user