第四章 升级通道建立:HTTPS、MQTT与连接策略

好,咱们进入第四章。这一章聊的是升级通道怎么建。说白了,就是升级包怎么从云端安全地送到各个域控制器手里。

我个人习惯把升级通道比作一条「数据管道」。管道建得不好,升级就容易翻车。我在项目中见过太多次因为通道问题导致的升级失败——有的是被中间人篡改了包,有的是连接频繁断开,还有的是并发升级时把网络打爆了。

这一章,咱们重点讲三个东西:HTTPS 升级通道MQTT 在 OTA 中的应用,以及长连接与短连接的选择策略

4.1 基于 HTTPS 的升级通道

HTTPS 是 OTA 升级中最常用的传输协议。为什么?因为它解决了两个核心问题:身份认证数据加密

你想想看,升级包如果明文传输,中间人随便改一个字节,ECU 刷进去就可能变砖。HTTPS 通过 TLS/SSL 层,保证了传输过程的机密性和完整性。

核心要点:HTTPS 升级通道的建立,本质上是客户端(ECU)与服务端(OTA 平台)之间的 TLS 握手过程。握手完成后,双方协商出对称密钥,后续所有数据都通过该密钥加密传输。

我在实际项目中,遇到过一个问题:某些老旧的 MCU 资源有限,TLS 握手计算量太大,导致握手超时。后来我们用了会话复用(Session Resumption)机制,才解决了这个问题。

下面是一个典型的 HTTPS 下载流程(伪代码):

// 伪代码:HTTPS 升级包下载流程
function downloadUpdatePackage(url, cert) {
    // 1. 建立 TLS 连接
    tlsSocket = createTlsSocket(cert);
    
    // 2. 发送 HTTP GET 请求
    httpRequest = new HttpRequest("GET", url);
    tlsSocket.send(httpRequest);
    
    // 3. 接收响应,校验 Content-Length
    response = tlsSocket.receive();
    if (response.statusCode != 200) {
        throw new Error("下载失败,状态码: " + response.statusCode);
    }
    
    // 4. 分块接收数据,边接收边校验哈希
    while (response.hasMoreData()) {
        chunk = response.readChunk(4096);
        updateHash(chunk);
        writeToFlash(chunk);
    }
    
    // 5. 最终校验完整包哈希
    if (finalHash != expectedHash) {
        throw new Error("哈希校验失败");
    }
    
    return SUCCESS;
}

这里要注意几个坑:

  • 证书管理:ECU 上要预置根证书,并且支持证书吊销列表(CRL)。我曾经见过一个项目,证书过期了还在用,结果升级全部失败。
  • 断点续传:大包下载时,网络中断是常事。HTTPS 本身不支持断点续传,需要应用层实现 Range 请求。
  • 并发控制:多个 ECU 同时下载,要考虑服务端带宽和连接数限制。

警告:不要为了省资源而禁用证书验证!我在某项目中发现,有人为了「加快升级速度」跳过了证书校验,结果被中间人攻击,刷入了恶意固件。这个教训很深刻。

4.2 MQTT 协议在 OTA 中的应用

MQTT 是轻量级的发布/订阅协议,特别适合 IoT 场景。在 OTA 中,MQTT 主要用来做控制信令,而不是传输升级包本身。

为什么不用 MQTT 传升级包?因为 MQTT 的 payload 大小有限制(通常 256KB),而且没有 HTTPS 那样的分块下载和断点续传能力。所以,常见的做法是:

  • MQTT 通道:下发升级指令、查询升级状态、上报进度
  • HTTPS 通道:传输升级包二进制数据

我习惯这样设计:

// MQTT 主题设计示例
// 云端 -> 车端
topic: ota/{vin}/command
payload: {
    "action": "start_update",
    "package_url": "https://ota.example.com/packages/xxx.bin",
    "package_hash": "sha256:xxxx",
    "target_ecu": ["gw", "bcm", "ic"]
}

// 车端 -> 云端
topic: ota/{vin}/status
payload: {
    "ecu_id": "gw",
    "status": "downloading",
    "progress": 45,
    "error_code": 0
}

MQTT 有几个关键特性,在 OTA 中非常有用:

  • QoS 等级:控制信令建议用 QoS 1(至少一次),确保指令不丢失。状态上报可以用 QoS 0(最多一次),减少网络开销。
  • 遗嘱消息:ECU 异常断开时,云端能及时感知,避免「假在线」问题。
  • 保留消息:新上线的 ECU 可以立即获取最新的升级状态,不用等云端重新下发。

小技巧:我在项目中,把 MQTT 的心跳间隔设为 30 秒。太短了浪费带宽,太长了无法及时发现断连。这个值可以根据实际网络环境调整。

4.3 长连接与短连接策略

这个问题,说白了就是「连接保持多久」的问题。长连接和短连接各有适用场景,选错了会出大问题。

短连接:每次通信都新建连接,用完就关。优点是简单、资源占用少。缺点是频繁握手,延迟高。

长连接:连接建立后一直保持,直到主动关闭或异常断开。优点是延迟低、适合实时通信。缺点是占用系统资源,需要心跳保活。

在 OTA 场景中,我的建议是:

场景 推荐策略 原因
升级包下载 短连接 下载完成后连接无意义,释放资源
控制信令 长连接 需要实时接收指令,低延迟
状态上报 短连接或长连接 取决于上报频率,高频用长连接
日志上传 短连接 数据量大,上传完即止

我曾经在一个项目中,所有通信都用长连接。结果车上有 30 多个 ECU,每个都保持长连接,网关的连接数直接爆了。后来我们改成:控制信令用 MQTT 长连接,下载和日志上传用 HTTPS 短连接,问题就解决了。

经验之谈:长连接不是越多越好。每个长连接都需要维护 TCP 状态、内存缓冲区、心跳定时器。在资源受限的嵌入式设备上,连接数要严格控制。我一般建议单个 ECU 的长连接不超过 3 个。

还有一个容易被忽略的点:连接复用。如果多个 ECU 需要通过同一个网关上网,网关可以复用与云端的连接,减少整体连接数。这就是「连接池」的思路。

嗯,这一章的内容就这些。总结一下:

  • HTTPS 负责安全传输升级包,注意证书管理和断点续传
  • MQTT 负责控制信令,轻量高效,但别用它传大文件
  • 长连接和短连接要按场景选,别一股脑全用长连接

下一章,咱们聊聊升级过程中的状态机设计和异常处理。这个很关键,搞不好升级到一半断电,车就趴窝了。