一、蓝牙连接数据包
BLE 连接过程中有三个重要的数据包:SCAN_REQ, SCAN_RSP 和 CONNECT_REQ。
- SCAN_REQ:
扫描请求,由主设备(MASTER DEVICE
)向从设备(SLAVE DEVICE
)发出,目的是为了获得从设备的响应以得到更多的从设备广播数据信息(包括设备名字,或者服务UUID
,及其它如厂家特定格式的信息(如硬件版本,软件版本号,设备系列号等等)。 - SCAN_RSP:
从设备对就主设备发起的SCAN_REQ的响应,作为广播包的补充,从设备可以给主设备更多的广播数据,比如说,有些设备在广播包里面没有设备名字,这个时候就可以把设备名字放在这个包里面发给主设备。 - CONNECT_REQ:
主设备向从设备发出连接请求。至此连接建立完成(从设备不会响应这个请求),如果从设备没有连接上面的问题的话,以后主从双方会开始相互交换有效数据(基于GAP,GATT
及SMP
协议)或者交换空包。
更多内容参见一分钟读懂低功耗蓝牙(BLE)连接数据包
二、什么是MTU
MTU(MAXIMUM TRANSMISSION UNIT
)最大传输单元,指在一个PDU(Protocol Data Unit
:协议数据单元,在一个单元中的有效传输数据)能够传输的最大数据量(多少字节可以一次传输给对方)。
三、MTU交换的意义(设置reqeustMtu的意义)
MTU
交换是为了在主从双方设置一个PDU中最大能够交换的数据量,通过MTU
的交换和双方确认(注意这个MTU
是不可以协商的,只是通知对方,双方在知道对方的极限后会选择一个较小的值作为以后的MTU。
比如说,主设备发出一个150个字节
的MTU
请求,但是从设备回应MTU
是23字节
,那么今后双方要以较小的值23字节
作为以后的MTU
),主从双方约定每次在做数据传输时不超过这个最大数据单元,MTU
交换通常发生在主从双方建立连接关系后。
更多内容移步一分钟读懂低功耗蓝牙(BLE)MTU交换数据包
四、如何妥善处理数据包大小(MTU)限制
Android
系统从4.3(API 18)
开始支持BLE
,且从5.1(API 21)
才开始支持MTU
修改(默认MTU
仅为23字节
,而且传输本身用掉3字节
)。
1.为什么BLE默认限制数据传输长度为20个字节?
core spec
里面定义了ATT的默认MTU
为23个bytes
,除去ATT
的opcode
一个字节以及ATT
的handle 2个字节
之后,剩下的20个字节
便是留给GATT
的了。
考虑到有些Bluetooth smart
设备功能弱小,不敢太奢侈的使用内存空间,因此core spec
规定每一个设备都必须支持MTU
为23
。
在两个设备连接初期,大家都像新交的朋友一样,不知对方底细,因此严格的按照套路来走,即最多一次发20个字节
,是最保险的。
由于ATT
的最大长度为512byte
,因此一般认为MTU
的最大长度为512个byte
就够了,再大也没什么意义,你不可能发一个超过512
的ATT
的数据,就像是孙猴子跑不过五行山一样。
所以ATT
的MTU
的最大长度可视为512个bytes
。
2.如何突破MTU 20字节的限制?
当我们需要传输 / 接收超过20字节长度的数据时,就需要在连接设备成功后通过BluetoothGatt#requestMtu(int mtu)
方法进行对应的请求设置,示例代码如下:
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
if (status == BluetoothGatt.GATT_SUCCESS) {
LogUtil.e(TAG, "request mtu success.约定后的MTU值为:" + mtu);
} else {
LogUtil.e(TAG, "request mtu failed.");
}
}
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {//蓝牙连接成功
gatt.requestMtu(39);
}
}
}
注意:上述代码的onMtuChanged
可以发挥关键作用。MTU
默认取的是23
,当收到onMtuChanged
后,会根据传递的值修改MTU
,注意由于传输用掉3
个字节,因此传递的值需要减3。
3.为什么专门记录MTU
记录MTU
主要是用于传输过程中判断数据是否需要分包发送。按照MTU
的大小严格约束每次发送的数据包大小,如果不这么做,很可能远端接收就会出错。除非你的数据包大小本身就很小。
4.设置完MTU后无法在BluetoothGattCallback#onConnectionStateChange
方法中立即发现设备服务列表
当我们设置完MTU后,发现在BluetoothGattCallback#onConnectionStateChange
方法中调用BluetoothGatt.discoverServices()
无法立即触发BluetoothGattCallback#onServicesDiscovered
回调。
我之前尝试过通过延迟调用BluetoothGatt#discoverServices()
方法(经测大概需要延迟1000ms-1200ms
)后才能够正常的获取到服务列表及发现相关特征,后来发现正确的解决方法应该是在BluetoothGattCallback#onMtuChanged
回调中调用BluetoothGatt#discoverServices()
方法即可解决问题。
#【本内内容参考自以下文章,侵删】
#【写在最后】
经测,
iOS
在9.0
以后能够正常的处理数据包过长的问题,CoreBluetooth
针对长数据已经内部做出了处理,不需要额外的多次调用getValue
方法,详见BLE:使用Android / iOS读取长特征值(BLE: Read Long Characteristics Value using Android / iOS)。