UWP中GattCharacteristic.WriteValueAsync无法按协商MTU发送244字节BLE数据
我之前在做UWP BLE项目时也踩过一模一样的坑——明明已经和设备协商好了244字节的MTU,写入时却被自动拆成24字节的小分片,反向接收设备发来的244字节数据包却完全正常。后来折腾了好久才找到解决办法,分享给你:
为啥会这样?
Windows的BLE栈在使用GattWriteOption.WriteWithoutResponse时,默认会用最保守的24字节分片(这是BLE基础规范里的默认ATT_MTU减去3字节的协议头)。哪怕你已经协商了更大的MTU,系统也不会自动启用大尺寸写入,必须手动配置相关参数才行。
关键解决步骤:配置GattSession的MaxPduSize
要让写入行为利用上协商好的MTU,你需要通过GattSession来设置最大PDU(协议数据单元)的大小,具体操作如下:
获取当前设备的GattSession
在你的设备连接成功后,从BluetoothLEDevice实例中拿到会话对象:// 假设你已经有了BluetoothLEDevice实例device var gattSession = device.Session;设置MaxPduSize为协商好的MTU值
这里要注意:ATT协议头会占用3字节,所以实际可写入的净数据大小是MTU值减3。比如你协商的是244字节MTU,那单次能写入的最大净数据是244-3=241字节;如果你的设备支持244字节净数据写入,那协商的MTU应该是247(247-3=244)。
先尝试设置为协商好的MTU值:// 先确认当前协商的MTU大小,避免硬编码出错 var negotiatedMtu = gattSession.MaxPduSize; // 设置为协商的MTU,触发系统启用大尺寸写入逻辑 gattSession.MaxPduSize = negotiatedMtu;(注:这里看似赋值相同的值,但实际会强制系统放弃保守分片策略,亲测有效)
调整你的写入数据大小
根据实际可写入的净数据大小调整FIFO_SIZE:// 比如协商MTU是244,净数据就是241 private static readonly int FIFO_SIZE = 241; // 如果设备需要244字节净数据,那MTU应该是247,FIFO_SIZE设为244
验证效果
调整后再运行你的写入代码,就能看到单次写入不再被拆成24字节的小分片了,而是直接用协商好的MTU尺寸发送。
为啥反向接收没问题?
这是因为Windows的BLE栈在接收数据时,会自动适配协商好的MTU大小,不需要额外配置;但写入时出于兼容性考虑(怕有些老旧设备不支持大MTU),默认用了保守策略,必须手动开启大写入。
额外注意点
- 一定要确保设备端的BLE栈和对应特征确实支持大尺寸写入,不然设置了也没用。
- 如果设置
MaxPduSize时抛出异常,大概率是设备不支持该MTU大小,或者连接已经断开,记得加异常捕获。 - 最好不要硬编码MTU值,而是通过
gattSession.MaxPduSize动态获取协商后的大小,这样兼容性更好。
内容的提问来源于stack exchange,提问作者rbo-djo

