UWP开发BLE外设:WriteRequested与ReadRequested事件多次迭代后无响应
UWP BLE外设文件传输:多次读写后Write/ReadRequested事件停止触发
我基于UWP开发BLE外设,需要向移动客户端传输文件。实现逻辑为:服务端将文件分块,客户端通过WriteAsync请求触发分块处理,再通过ReadAsync请求获取处理后的响应。客户端流程是先获取分块数量,再循环对应次数,每次发送WriteAsync并通过ReadAsync接收响应。但目前遇到问题:经过几次迭代后,WriteRequested与ReadRequested事件不再被触发。
问题代码
private GattLocalCharacteristic fileListCharacteristic; private string exceptionString = "Exception: "; private Guid characteristicGuid = new Guid("xxxxxx"); private Guid serviceGuid = new Guid("xxxxxx"); private ResponseModel responseModel; private List<byte[]> data; private byte[] chunkBuffer; // 创建GATT服务提供者 GattServiceProviderResult serviceProviderResult = await GattServiceProvider.CreateAsync(serviceGuid); if (serviceProviderResult.Error == BluetoothError.Success) { GattServiceProvider serviceProvider = serviceProviderResult.ServiceProvider; // 创建GATT特征 GattLocalCharacteristicParameters characteristicParameters = new GattLocalCharacteristicParameters { CharacteristicProperties = GattCharacteristicProperties.Read | GattCharacteristicProperties.WriteWithoutResponse | GattCharacteristicProperties.Notify, UserDescription = "File List Characteristic" }; GattLocalCharacteristicResult characteristicResult = await serviceProvider.Service.CreateCharacteristicAsync(characteristicGuid, characteristicParameters); if (characteristicResult.Error == BluetoothError.Success) { fileListCharacteristic = characteristicResult.Characteristic; fileListCharacteristic.ReadRequested += FileListCharacteristicReadRequested; fileListCharacteristic.WriteRequested += FileListCharacteristicWriteRequested; // 开始广播GATT服务 serviceProvider.StartAdvertising(); } else { Console.WriteLine("Error creating GATT characteristic: " + characteristicResult.Error); } } else { Console.WriteLine("Error creating GATT service: " + serviceProviderResult.Error); } private async void FileListCharacteristicWriteRequested(GattLocalCharacteristic sender, GattWriteRequestedEventArgs args) { var deferral = args.GetDeferral(); GattWriteRequest request = await args.GetRequestAsync(); DataReader reader = DataReader.FromBuffer(request.Value); byte[] dataBytes = new byte[reader.UnconsumedBufferLength]; reader.ReadBytes(dataBytes); string dataString = Encoding.UTF8.GetString(dataBytes); if (string.IsNullOrWhiteSpace(dataString)) return; responseModel = JsonConvert.DeserializeObject<ResponseModel>(dataString); if (request.Option == GattWriteOption.WriteWithResponse) { request.Respond(); } deferral.Complete(); } private async void FileListCharacteristicReadRequested(GattLocalCharacteristic sender, GattReadRequestedEventArgs args) { var deferral = args.GetDeferral(); string responseString = "OK"; switch (responseModel.ResponseCode) { case 1: List<FileInfoModel> fileList = await GetFileList(); responseString = SerializeFileList(fileList); break; case 2: byte[] fileBuffer = await GetFileContent(responseModel.ResponseMessage); int bufferChunkSize = 508; int ChunksCount = (int)Math.Ceiling((double)fileBuffer.Length / bufferChunkSize); data = DivideByteArray(fileBuffer,bufferChunkSize); ChunkInfo chunkInfo = new ChunkInfo() { FullPath = responseModel.ResponseMessage, ChunkCount = ChunksCount }; responseString = JsonConvert.SerializeObject(chunkInfo); break; case 3: int currentChunk = responseModel.CurrentChunk; chunkBuffer = data[currentChunk]; break; default: responseString = "OK"; break; } IBuffer buffer = responseModel.ResponseCode == 3 ? chunkBuffer.AsBuffer() : Encoding.UTF8.GetBytes(responseString).AsBuffer(); GattReadRequest request = await args.GetRequestAsync(); request?.RespondWithValue(buffer); deferral.Complete(); }
问题排查与修复建议
全局变量线程安全问题
你使用了responseModel、data、chunkBuffer等全局共享变量,但BLE事件回调在后台线程触发,并发读写会导致状态混乱,甚至引发未处理异常,最终导致事件订阅失效。- 修复:用
lock块保护全局变量的访问,或者将请求状态绑定到单个请求上下文,避免共享状态。
- 修复:用
Deferral未正确完成
在WriteRequested中,如果dataString为空直接return,未调用deferral.Complete(),会导致系统认为请求未处理,阻塞后续请求。- 修复:用
try/finally或using确保deferral始终被完成。
- 修复:用
未处理异常导致事件处理器失效
ReadRequested中若responseModel未初始化(客户端先读后写),访问responseModel.ResponseCode会抛出空引用异常,未捕获的异常会让系统移除事件处理器,后续事件不再触发。- 修复:添加空值检查,捕获并记录异常,确保事件处理器不会崩溃。
读写请求响应不完整
对于WriteWithResponse请求,无论处理成功与否都应返回响应;分块索引无效时,需返回BLE协议错误码,避免客户端等待超时。
修改后的关键代码示例
修复后的WriteRequested事件
private async void FileListCharacteristicWriteRequested(GattLocalCharacteristic sender, GattWriteRequestedEventArgs args) { using var deferral = args.GetDeferral(); try { GattWriteRequest request = await args.GetRequestAsync(); DataReader reader = DataReader.FromBuffer(request.Value); byte[] dataBytes = new byte[reader.UnconsumedBufferLength]; reader.ReadBytes(dataBytes); string dataString = Encoding.UTF8.GetString(dataBytes); if (!string.IsNullOrWhiteSpace(dataString)) { lock (this) { responseModel = JsonConvert.DeserializeObject<ResponseModel>(dataString); } } if (request.Option == GattWriteOption.WriteWithResponse) { request.Respond(); } } catch (Exception ex) { Console.WriteLine($"{exceptionString}{ex.Message}"); var request = await args.GetRequestAsync(); if (request?.Option == GattWriteOption.WriteWithResponse) { request.RespondWithProtocolError((byte)GattProtocolError.UnlikelyError); } } }
修复后的ReadRequested事件
private async void FileListCharacteristicReadRequested(GattLocalCharacteristic sender, GattReadRequestedEventArgs args) { using var deferral = args.GetDeferral(); try { ResponseModel localResponseModel; List<byte[]> localData; lock (this) { if (responseModel == null) { var request = await args.GetRequestAsync(); request?.RespondWithProtocolError((byte)GattProtocolError.InvalidPdu); return; } localResponseModel = responseModel; localData = data; } string responseString = "OK"; byte[] chunkBuffer = null; switch (localResponseModel.ResponseCode) { case 1: List<FileInfoModel> fileList = await GetFileList(); responseString = SerializeFileList(fileList); break; case 2: byte[] fileBuffer = await GetFileContent(localResponseModel.ResponseMessage); int bufferChunkSize = 508; int chunksCount = (int)Math.Ceiling((double)fileBuffer.Length / bufferChunkSize); var dividedData = DivideByteArray(fileBuffer, bufferChunkSize); lock (this) { data = dividedData; } ChunkInfo chunkInfo = new ChunkInfo() { FullPath = localResponseModel.ResponseMessage, ChunkCount = chunksCount }; responseString = JsonConvert.SerializeObject(chunkInfo); break; case 3: int currentChunk = localResponseModel.CurrentChunk; if (localData != null && currentChunk >= 0 && currentChunk < localData.Count) { chunkBuffer = localData[currentChunk]; } else { var request = await args.GetRequestAsync(); request?.RespondWithProtocolError((byte)GattProtocolError.InvalidOffset); return; } break; default: responseString = "OK"; break; } IBuffer buffer = localResponseModel.ResponseCode == 3 && chunkBuffer != null ? chunkBuffer.AsBuffer() : Encoding.UTF8.GetBytes(responseString).AsBuffer(); GattReadRequest request = await args.GetRequestAsync(); request?.RespondWithValue(buffer); } catch (Exception ex) { Console.WriteLine($"{exceptionString}{ex.Message}"); var request = await args.GetRequestAsync(); request?.RespondWithProtocolError((byte)GattProtocolError.UnlikelyError); } }
内容的提问来源于stack exchange,提问作者Santos
相关产品推荐
相关产品推荐

