You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过for循环实现BLE数据发送并解决仅首条发送成功问题

问题原因

CoreBluetooth的CBPeripheralManager内部维护了发送通知的缓冲区,你直接用for循环调用updateValue的执行速度远快于BLE数据包的实际空中传输速度,第一个数据包发出后缓冲区直接被占满,后续调用都会直接返回false,对应数据直接被丢弃不会进入发送队列。

解决方案

你需要自己维护一个待发送数据队列,结合系统提供的缓冲区可用回调来实现连续传输,具体实现逻辑如下:

1. 声明所需属性

// 待发送数据队列
private var pendingSendQueue = [Data]()
// 你原有的特征属性
private var transferCharacteristic: CBMutableCharacteristic?

2. 封装发送触发方法

private func triggerSend() {
    guard let characteristic = transferCharacteristic else { return }
    // 持续从队列头部取数据发送,直到队列为空或者缓冲区满
    while !pendingSendQueue.isEmpty {
        let nextData = pendingSendQueue.first!
        let sendSuccess = peripheralManager.updateValue(nextData, for: characteristic, onSubscribedCentrals: nil)
        if sendSuccess {
            print("Sent: \(String(data: nextData, encoding: .utf8) ?? "")")
            pendingSendQueue.removeFirst()
        } else {
            // 缓冲区已满,停止发送,等待系统回调通知缓冲区可用
            break
        }
    }
}

3. 改造你的发送逻辑

不要直接循环调用updateValue,先把所有待发数据加入队列,再触发一次发送:

guard let characteristic = self.transferCharacteristic else { return }
// 所有待发数据先入队
for x in "Hello there" {
    guard let data = "\(x)".data(using: .utf8) else { continue }
    pendingSendQueue.append(data)
}
// 触发发送
triggerSend()

4. 实现系统代理回调

实现CBPeripheralManagerDelegate的对应方法,在系统通知缓冲区可用时继续发送队列剩余数据:

func peripheralManagerIsReady(toUpdateSubscribers peripheral: CBPeripheralManager) {
    triggerSend()
}
优化方案

如果要提升传输效率,可以做如下优化:

  • 不要单字节发送,尽量每次发送数据的长度接近当前连接的MTU最大值,iOS设备默认BLE连接MTU在185字节左右,单次发送尽可能多的数据可以大幅降低传输开销
  • 如果需要保证数据传输可靠性,可以自定义传输协议,给每个数据包增加序号和校验位,对端收到后回传ACK确认,避免丢包
  • 针对高速传输场景,可以适当调整队列的缓存策略,避免内存占用过高

内容的提问来源于stack exchange,提问作者Visal Rajapakse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 16:09:01