我在外围设备上使用两个 ATT 特性实现了双工 BLE 通信(本质上,类似于串行 TX/RX 电缆)——一个是可写的,另一个是可读的,带有通知。除了报告的写入速度外,一切都按预期工作。
当我调用下面的代码时:
txCharacteristic.setValue(data);
boolean queuedOk =
connectedPeripheralGatt.writeCharacteristic(txCharacteristic);
onCharacteristicWrite
回调几乎立即被触发,然后我重复上面的代码 fragment 以在特性上应用下一个数据 fragment 。当接收方报告正确的速度(大约 10 KB/s)时,根据 onCharacteristicWriteRequest
中收集的时间戳测量,发送方报告令人难以置信的速度(如 50 - 300 KB/s)并且它报告它已完成将所有数据推送到 writeCharacteristic
,尽管接收方仍在接收传入数据。
因此,显然 onCharacteristicWriteRequest
正在被调用,而大部分数据仍在发送到目标设备的途中。
当我将数据从外围设备发送到中央设备时,同样的事情发生了,只是这次 onNotificationSent
在外围设备上是关于它的速度和 onCharacteristicChanged
在中央正在报告正确的速度。
是否有任何或多或少可靠的方法来近似测量来自发送方的传出数据速率而不打开 ACK(出于性能原因我不想这样做)?
数据本身完好无损,我正在使用 0.5 - 5 兆字节大小的图像文件对其进行测试,并且图像始终在接收端正确解码。
为了最大化吞吐量,可写特性被设置为不可靠的,如下所示:
BluetoothGattCharacteristic rxCharacteristic = new BluetoothGattCharacteristic(UUID.fromString(RX_CHARACTERISTIC_UUID),
BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
BluetoothGattCharacteristic.PERMISSION_WRITE);
rxCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
我不确定我对 PROPERTY_WRITE_NO_RESPONSE
和 WRITE_TYPE_NO_RESPONSE
的使用,但从某些网站上的一些文章来看,模糊地似乎同时指定两者是一个好习惯;如果我错了,请纠正我。
我明白,通过这样的设置,我不能指望接收方对数据进行真正的确认,但我仍然希望 onCharacteristicWrite
和 onNotificationSent
本身至少会提供真实的数据定时检测数据何时实际从源设备发送出去。相反,似乎有一个相当大的缓存消耗我的数据并报告它几乎立即发送。
最佳答案
你是对的。
如果您使用“write with response”,则在远程发回写响应后将调用 onCharacteristicWrite 回调。但是,如果您使用“write without response”,Android 有一个流量控制机制可以缓冲您写入的数据。它是这样工作的:
应用写入数据包。蓝牙堆栈将此数据包放入其内部缓冲区。如果将此数据包入队后缓冲区中仍有更多空间,则会调用 onCharacteristicWrite 回调,因此您可以立即写入另一个数据包。当缓冲区已满时,它会等待调用 onCharacteristicWrite 回调,直到缓冲区中现在有 50% 的可用空间(如果我没记错的话)。
只要内部缓冲区中有数据包,它就会尝试将它们写入蓝牙 Controller ,蓝牙 Controller 也可以缓冲有限数量的数据包。蓝牙 Controller 通过 HCI 将“完成的数据包数”事件发送回 Android 的蓝牙堆栈,这表明远程设备的链路层已确认数据包。 (这并不表示远程应用程序没有收到它;只是远程蓝牙 Controller 收到了它。)如果蓝牙 Controller 中没有可用空间,它将等待直到有空间。
这比它在 iOS 上的工作方式要聪明得多。在那里,如果您发送大量“write with response”数据包,如果内部缓冲区已满,它们甚至会在发送之前被丢弃。 (这可以通过每 10 个数据包左右发送“write with response”来解决)。
不幸的是(对于您的情况),当数据包已被远程链路层确认时,Android 的蓝牙堆栈不会向应用程序发送回调,因此您必须根据 onCharacteristicWrite 回调来确定速度。但是,您可以每隔 10 个数据包左右向另一个方向发送一次状态通知,我认为这会给您带来良好的效果。如果 Android 的蓝牙堆栈在收到链路层确认时转而发送 onCharacteristicWrite 回调,这会将速度降低到每个连接事件仅一个数据包。
如果链接有时由于监督超时而断开连接,您应该知道我前段时间发布的错误报告:https://issuetracker.google.com/issues/37121017 .
关于您的属性/权限问题,这可能是同一件事,但蓝牙团队决定将它们分开。权限与 ATT 协议(protocol)有关,告诉蓝牙堆栈允许客户端做什么。属性只是对方可以读取的一些属性,以便知道它是什么类型的特征。
关于android - onCharacteristicWrite 和 onNotificationSent 被调用得太快 - 如何获取实际传出数据速率?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43741849/