前面文章分别说了移动云电脑公众版DD WIN11专业版

用EasyTier 自建共享节点组网远控云电脑

但是使用下来,云电脑一直关机,不能随时链接的云电脑意义在哪里呢?于是

研究了一下保活协议

目前只研究了公众版H3C(新华三)云电脑桌面协议,ZTE没有机器没有研究

在研究之前,我查找了github一些信息,发现有几个项目说通过desktopUptime控制面查询。它能证明 Token 有效、桌面存在、API 可访问,但不能证明客户端与云桌面之间存在 Display/SPICE 数据面连接。因此它不能作为“云电脑不会因无人连接而关机”的充分条件。

于是乎,开始自己动手

最初的保活方式是调用官方 H3C Session 程序。它确实能够建立桌面会话,但也会同时启动官方二进制、IPC 服务和远程桌面相关能力,效果接近真正打开一次移动云电脑

这种方式存在几个问题:

  1. 资源占用高:会启动额外进程,并进入完整 Display 会话流程。
  2. 部署复杂:依赖 Windows EXE、DLL、运行目录和厂商 SDK。
  3. 不适合 Docker:容器中不应为了保活再运行完整 GUI 或 Windows 会话程序。
  4. 故障难解释:只知道“官方程序断开了”,不清楚失败发生在认证、分配、上线还是心跳阶段。

因此目标被重新定义为:

不渲染桌面,只完成服务端认为“终端已认证、云电脑已上线、客户端仍然存活”所需要的最小协议流程。

用“酒店入住”理解整个协议

可以把云电脑连接过程理解成酒店入住:

协议动作酒店类比作用
移动云 HTTP 登录在平台取得订单和身份证明获取 AccessToken、设备和云电脑信息
获取桌面连接参数查看酒店地址得到 H3C Broker 地址、桌面池、机器标识等
WhoAreYou确认前台身份判断目标端口确实是 H3C Broker
queryDeviceConfigInfo询问入住规则获取服务端设备和认证配置
RegisterDevice登记终端服务端记录当前终端身份
CheckPassword核验入住凭据确认用户和桌面凭据
ApplyForVm申请房间从桌面池申请或定位云电脑
OperateForVm办理正式入住让目标云电脑进入连接状态
ReportSuccess告知前台入住成功建立服务端认可的在线状态
Heartbeat定期报平安防止服务端认为客户端已经离线

最终有效的路线是:

官方成功会话
→ 收集 IPC、Verbose 日志和网络证据
→ 找出 Broker 地址、TLS 参数和 RPC 名称
→ 加载 protobuf descriptor
→ 用 Python 重放只读 RPC
→ 分阶段还原认证状态机
→ 对照官方成功字段修正 Apply/Operate
→ 验证 ReportSuccess 和 Heartbeat
→ 加入重连、退避、脱敏报告和 Docker

第一层:业务控制面证据

先通过移动云 HTTP API 获得:

  • 当前账号是否登录成功。
  • 目标云电脑是否存在。
  • 桌面实例、桌面池、机器 UUID 等信息。
  • customLoginParams 或同类字段中的 Broker 地址。

如果控制面信息已经错误,后面的 H3C 协议不会成功。

第二层:网络证据

按顺序验证:

  1. 本机到 Broker 的路由是否存在。
  2. TCP 端口是否可连接。
  3. TLS 握手是否成功。
  4. gRPC WhoAreYou 是否返回预期服务名称。

TCP 成功只代表端口可达,不代表 TLS、gRPC 和业务认证成功。

本项目研究中曾出现物理机能够访问 Broker,而云电脑内部系统不能访问 Broker 的情况。这说明保活程序必须运行在具备正确网络路由的位置;Docker 也必须继承或配置相同的网络可达性。

第三层:协议结构证据

通过官方 protobuf descriptor 可以确认:

  • RPC 路径。
  • 请求和响应消息类型。
  • 字段名称。
  • 字段的数据类型和嵌套关系。

descriptor 能告诉我们“报文长什么样”,但不能单独告诉我们“业务上应该填什么值”。业务值仍需通过官方成功日志和对照实验确认。

第四层:成功会话证据

成功 trace 的价值最高。对每个 RPC 建议记录:

项目说明
RPC 名称例如ApplyForVm
请求时间用于确定调用顺序
请求字段必须脱敏,只保留结构和值的类型
响应字段重点观察下一阶段需要复用的值
服务端状态成功、gRPC 错误或业务错误码
结论哪些字段是固定值,哪些来自前一步响应

最终确认的 H3C 状态机

当前生产代码使用的状态机为:

flowchart LR
    A["WhoAreYou"] --> B["queryDeviceConfigInfo"]
    B --> C["RegisterDevice"]
    C --> D["CheckPassword"]
    D --> E["ApplyForVm"]
    E --> F["OperateForVm"]
    F --> G["ReportSuccess"]
    G --> H["Unary Heartbeat"]
    H -->|"成功"| H
    H -->|"失败"| I["关闭旧通道"]
    I --> J["指数退避"]
    J --> A

注意事项:

终端身份必须前后一致

服务端并不是只在 RegisterDevice 阶段检查设备。后续 CheckPasswordApplyForVmOperateForVm 和心跳仍会复用终端身份。

当前实现统一维护:

  • local_ip
  • mac
  • device_id
  • device_name
  • computer_type
  • protocol_type
  • client_type

官方成功行为表明,当前 H3C 链路中 device_id 应与规范化后的 MAC 保持一致,protocol_type 使用 vdp。如果只修改注册阶段,而后续 RPC 又换回另一组身份,常见结果就是“未注册”或“无效请求”。

1. Apply 和 Operate 不能混为一谈

ApplyForVm 是申请或定位云电脑,OperateForVm 才是正式执行连接操作。

已确认的关键原则:

阶段关键规则
ApplyForVm请求中不提前发送最终vm_id
ApplyForVmid 使用桌面池标识,pool_name 使用桌面池名称
ApplyForVmport="0"start_domain=1token_flag=false
Apply 响应从响应中取得真正的vm_id、主机 IP 或密码信息
OperateForVm复用 Apply 返回的vm_id
OperateForVmop_type=3force_flag=2
OperateForVmport="0"start_domain=1refresh_flag="1"
ReportSuccess使用上线后 VM 地址,并使用refresh_flag="0"
Heartbeat复用已认证上下文,携带vm_idrefresh_flag="0"

这些字段不是通过枚举猜出来的,而是通过官方成功 trace 与失败请求对比得到的。


2. 典型错误码如何理解
2.1 80004: For input string

通常表示服务端尝试把某字段解析为数字,但客户端发送了文字。例如把产品名、客户端名或协议名放进了期望数字的字段。

处理方法:

  1. 先查 descriptor 中该字段的 protobuf 类型。
  2. 再对照官方请求中同一字段的实际值。
  3. 不要仅根据字段英文名称猜含义。
2.2 80002: 无效请求

这是最常见也最宽泛的业务错误。它可能表示:

  • RPC 顺序错误。
  • 终端身份前后不一致。
  • 字段类型正确但业务组合错误。
  • 把后续阶段的值提前放进当前请求。
  • 缺少前一步服务端生成的上下文。

正确做法是退回到最后一个成功步骤,对照成功 trace,而不是同时改五六个参数。

2.3 80068: 设备未注册

说明后续请求使用的身份没有被服务端识别为刚才注册的设备。重点检查:

  • 注册和认证阶段的 device_id 是否相同。
  • MAC、IP、设备类型是否发生变化。
  • 是否误把 register_id 当成 device_id
  • 是否在不同主机或不同网络环境中切换了身份。
2.4 80619: 当前设备已作为 VDI 类型设备使用

说明服务端已经保存了当前终端的注册类型。此时不应继续大量重复注册。应确认官方客户端使用的设备类型,必要时由有权限的管理人员解除旧注册。

2.5 gRPC UNKNOWN 或空错误

优先检查:

  • TLS CA 文件是否正确。
  • gRPC authority 是否与证书匹配。
  • descriptor 是否与服务端版本匹配。
  • metadata,例如语言字段是否正确。
  • RPC 超时和网络中断。

空错误不等于“随便换业务字段”。

image.png

有需要源代码学习研究的可以评论区留言联系我,留言时候留下邮箱哦,仅供学习研究使用

最后修改:2026 年 07 月 20 日
感谢大哥送来的卡布奇诺和冰阔乐!