3.3 KiB
3.3 KiB
WebSocket 异步命令输出修复方案
问题描述
Web 前端的命令终端页面中,datacenter in 等同步命令能正确显示输出,但 self_ptl 6 等异步命令的输出不会传输到前端。
原因分析
ws_cmd_exec() 通过重定向 STDOUT → pipe → reader 线程的方式捕获命令输出:
ws_cmd_exec() → dup2(pipe, stdout) → p_cmd->func() → dup2(saved_stdout, stdout) → join reader
- datacenter in:
cmd_dc()直接调用dc_show_in_signals(),使用std::cout/printf同步输出到 stdout → 被 pipe 捕获 ✅ - self_ptl 6:
cmd_self_ptl()仅设置g_flag[6]=1,立即返回。实际数据经test_self_check_get()→icp67_self_check_get_cb()→ ICP67 协议发送 → 远端响应 →self_check_pop_out()异步回调处理。该回调仅输出LOG_I日志,不走 stdout → 不被 pipe 捕获 ❌
解决方案
核心思路
在 WebSocket 服务端维护一个 <命令名 → WebSocket 连接 ID> 的 Map 映射,让异步回调可以根据命令名查找到对应的连接 ID,将结果推送回前端。
架构设计要点
- 跨模块解耦:在
mySystem.h中声明共享 API(ws_set_async_cmd_conn、ws_get_async_cmd_conn、ws_send_binary),各模块只需包含mySystem.h即可使用,无需跨模块引用头文件 - 读写接口封装:用
ws_set_async_cmd_conn()/ws_get_async_cmd_conn()取代直接暴露全局变量,内部用 mutex 保护 - 多连接支持:使用
std::map<std::string, unsigned long>以命令名为 key 存储,不同命令互不干扰。同一命令的后续请求会覆盖前一个
修改文件
| 文件 | 修改内容 |
|---|---|
src/system/libweb_server/src/ws_method.cpp |
用 std::map + mutex 替代单一全局变量,添加 ws_set/ws_get_async_cmd_conn() 接口 |
src/system/libweb_server/inc/ws_method.h |
移除 extern unsigned long g_async_cmd_conn_id |
release/inc/mySystem.h |
添加 ws_set_async_cmd_conn()、ws_get_async_cmd_conn()、ws_send_binary() 跨模块声明 |
src/system/libself_ptl/src/method.cpp |
移除 #include "ws_method.h",改为 #include "mySystem.h",使用 ws_get_async_cmd_conn("self_ptl") |
release/src/system/libself_ptl/makefile |
移除跨模块包含路径 -I $(SRC_ROOT_DIR)/system/libweb_server/inc |
数据流
前端 WebSocket 键入 self_ptl 6 (conn_id=100)
→ ws_recv() 提取命令名 "self_ptl"
→ ws_set_async_cmd_conn("self_ptl", 100)
→ map["self_ptl"] = 100
→ ws_cmd_exec() 返回初始输出 → ws_send_binary(初始输出)
同一时刻另一用户键入 datacenter in (conn_id=200)
→ ws_set_async_cmd_conn("datacenter", 200)
→ map["datacenter"] = 200 (不会覆盖 self_ptl 的条目 ✅)
(异步) ICP67 响应到达 → self_check_pop_out()
→ conn_id = ws_get_async_cmd_conn("self_ptl") → 100
→ ws_send_binary(100, 格式化结果) → 第一个用户前端显示 ✅
多连接场景
- 同一命令(如
self_ptl 6)被多个标签页先后执行 → 后续的会覆盖之前的 conn_id → 异步结果只推送给最后一个执行者 - 不同命令互不干扰,各自独立存储 conn_id
- 如需进一步优化(如广播到所有标签页),可在 map 中存储
std::vector<unsigned long>实现多连接推送