一、蓝牙连接数据包
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)。