RTU/claude/工程/libweb_server模块分析.md

9.1 KiB
Raw Permalink Blame History

libweb_server 模块分析

日期2026-06-10


1. 模块概览

libweb_server 是 RTU 的嵌入式 Web 服务器模块(app_web_server 线程),属于系统层的第 7 号线程。基于 Mongoose v7.21,提供 HTTP 静态文件服务 + WebSocket 实时数据通道。

目录结构

src/system/libweb_server/
├── inc/
│   ├── web_server.h     # 对外接口app_web_server_init1/2, app_web_server
│   └── ws_method.h      # WebSocket 消息处理方法声明
└── src/
    ├── web_server.cpp    # HTTP/WS 服务器 + mongoose 事件循环
    └── ws_method.cpp     # WebSocket 命令解析 + JSON 数据推送

2. 架构

2.1 线程模型

┌─────────────────────────────────────────────────┐
│  app_web_server 线程 (RTU 9号线程)                │
│                                                  │
│  init1: web_server_init()                        │
│    → mg_mgr_init + mg_http_listen(8000)          │
│    → pthread_create → web_server_run (独立线程)    │
│                                                  │
│  init2: (空)                                     │
│                                                  │
│  fun_cb: 事件循环 (3个定时器)                      │
│    EV_TIMER3 → ws_task() (每秒推送数据)            │
└─────────────────────────────────────────────────┘
         │
         │ g_ws_conns (连接列表) + g_ws_sessions (会话资源)
         │
┌─────────────────────────────────────────────────┐
│  web_server_run 线程 (mongoose 事件线程)           │
│                                                  │
│  while(1) mg_mgr_poll(500ms)                     │
│    → web_server_task() 回调                       │
│      MG_EV_HTTP_MSG → WS升级 / 静态文件            │
│      MG_EV_WS_MSG   → 接收命令                     │
│      MG_EV_CLOSE    → 断连清理                     │
└─────────────────────────────────────────────────┘

2.2 会话隔离模型

每个 WebSocket 连接拥有独立的 stru_ws_session,包含自己的五类信号集。连接建立时开辟、断开时释放。

客户端A (WebSocket) ─→ session_A: {out_signals_A, in_signals_A, yk_signals_A, ...}
客户端B (WebSocket) ─→ session_B: {out_signals_B, in_signals_B, yk_signals_B, ...}

ws_task() 遍历所有 session为每个 session 构建独立的 JSON 并发送到对应连接。

2.3 数据流

浏览器 ←── HTTP ──→ mongoose ←── 静态文件 (web_root/)
浏览器 ←── WS ────→ mongoose ←── JSON 命令/数据 ←── ws_method.cpp
                                        ↕
                                   DataCenter

3. web_server.cpp 核心逻辑

3.1 全局状态

g_web_root          静态文件根目录(进程目录 + "web_root"
mgr                 mongoose 事件管理器
g_ws_conns          vector<mg_connection*> 活跃 WebSocket 连接列表
g_ws_conns_mutex    pthread 互斥锁,保护 g_ws_conns
g_ws_sessions       map<conn_id, stru_ws_session> 每连接独立信号资源(ws_method.cpp
g_ws_session_mutex  pthread 互斥锁,保护 g_ws_sessions

3.2 事件处理 (web_server_task)

事件 处理
MG_EV_HTTP_MSG URI=/wsmg_ws_upgrade() 升级为 WebSocket其他 → mg_http_serve_dir() 静态文件
MG_EV_WS_MSG 将连接加入 g_ws_conns,消息经 std::string 安全复制后传给 ws_recv()
MG_EV_WS_CTL CLOSE 帧 → 调用 ws_session_destroy() + 从 g_ws_conns 移除
MG_EV_CLOSE 调用 ws_session_destroy() + 从 g_ws_conns 移除

3.3 ws_send_all / ws_send_one

  • ws_send_all():遍历 g_ws_conns 广播,跳过死连接
  • ws_send_one(conn_id, ...):按 c->id 精确定位单连接发送

均加 g_ws_conns_mutex 锁保护。


4. ws_method.cpp 命令处理

4.1 会话结构

struct stru_ws_session {
    vector<stru_ws_signal> out_signals;   // 本连接订阅的遥信
    vector<stru_ws_signal> in_signals;    // 本连接订阅的遥测
    vector<stru_ws_signal> yk_signals;    // 本连接订阅的遥控
    vector<stru_ws_signal> ao_signals;    // 本连接订阅的定值
    vector<stru_ws_signal> param_signals; // 本连接订阅的参数
};

全局 g_ws_sessions: map<conn_id, stru_ws_session> 管理所有连接的独立资源。g_ws_session_mutex 保护。

4.2 信号模型

每个连接 add 信号时仅影响自己的 session不同客户端之间完全隔离。

类型 session 字段 支持操作
out (遥信) session.out_signals add / del / set
in (遥测输入) session.in_signals add / del
yk (遥控) session.yk_signals add / del / set (含 SBO)
ao (定值) session.ao_signals add / del / set (含 SBO)
param (参数) session.param_signals add / del / set (含 SBO, 多定值区)

4.2 WebSocket 上行命令格式 (JSON)

{
    "curd": "add|del|set",
    "signal_type": "out|in|yk|ao|param",
    "saddr": "st.0",
    "signal_data": "1.5",
    "setting_zone": "0"
}

4.3 下行数据格式 (JSON)

每秒 (EV_TIMER3) 推送完整快照,五类信号分数组。修复后仅在值变化时推送。

4.4 SBO 控制流程

遥控/定值/参数支持 DIRECT_NORMAL(直接执行)和 SBO_NORMAL(选择-执行两步),通过 DataCenter 的 dc_signal_yk_set_status / dc_signal_ao_set_val / dc_signal_param_set_val 执行。


5. 缺陷与修复

5.1 缺陷清单(修复前)

# 严重度 位置 问题
1 严重 web_server.cpp L14 p_conn 单指针,仅支持一个 WS 客户端
2 严重 web_server.cpp MG_EV_CLOSE 处理,断连后悬空指针
3 严重 web_server.cpp p_conn 无锁保护,多线程竞态
4 严重 web_server.cpp L66, ws_method.cpp L519 mg_str 非 null-terminated 传给 printf/cJSON_ParseUB
5 高危 web_server.cpp L66 调试 printf 遗留
6 高危 ws_method.cpp L386 SBO task_sleep_ms(1000) 阻塞事件循环
7 中危 无心跳保活机制
8 中危 ws_method.cpp 每秒全量推送,数据不变也发送
9 中危 ws_method.cpp L400,L502 LOG_E("%d") 缺少对应参数
10 中危 web_server.cpp L22 ws_send 未校验 is_websocket/is_draining
11 严重 ws_method.cpp L21-25 多客户端共享同一套全局信号资源,客户端之间信号干扰

5.2 修复措施

第一轮2026-06-10

# 修复
1 p_conng_ws_conns: vector<mg_connection*>,遍历广播
2 新增 MG_EV_CLOSE + MG_EV_WS_CTL(CLOSE) 从列表移除
3 新增 pthread_mutex_t g_ws_mutex,列表读写前加锁
4 std::string(wm->data.buf, wm->data.len) 安全复制后再用
5 printfLOG_I
6 删除 task_sleep_ms(1000)
7 当前 500ms poll 周期可替代心跳mg_timer 需配合 wakeup_init 略复杂暂不引入
8 stru_ws_signal 新增 last_valws_task() 仅在值变化时发送
9 补全 p_signal->ctrl_type 参数
10 ws_send() 循环中增加 c->is_websocket && !c->is_draining 检查

第二轮2026-06-10会话隔离重构

# 修复
11 引入 stru_ws_session 每连接独立信号集,全局 g_ws_sessions: map<conn_id, session> 管理
ws_recv(c, ...) 增加连接参数,操作仅影响对应 session
ws_task() 遍历 sessions为每个连接构建独立 JSON → ws_send_one(conn_id, ...)
ws_session_destroy(c)MG_EV_CLOSE/WS_CTL(CLOSE) 时释放该连接所有信号资源
所有 add/del/set/make 函数改为接受 stru_ws_session& 参数,无状态纯函数

6. 对外接口

函数 文件 说明
app_web_server_init1(arg) web_server.cpp 第一段初始化,启动 HTTP/WS 服务器
app_web_server_init2(arg) web_server.cpp 第二段初始化(空)
app_web_server(arg) web_server.cpp 主线程循环,定时器驱动 ws_task()
ws_send_all(p_tx, tx_len) web_server.cpp 向所有 WS 客户端广播相同数据
ws_send_one(conn_id, p_tx, tx_len) web_server.cpp 向单个 WS 连接发送数据
ws_recv(c, p_rx, rx_len) ws_method.cpp 解析 WS 命令 JSON操作仅在 c 对应 session 内生效
ws_task() ws_method.cpp 遍历所有 session各自构建增量 JSON 并推送
ws_session_destroy(c) ws_method.cpp 释放连接对应的所有信号资源