如何通过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
相关产品推荐
相关产品推荐

