串口通信连续收发:如何实现逐包校验通过后再发送下一包
串口多包收发串行校验实现方案
核心解决思路
放弃同步循环发送的方式,改用异步串行执行模式:每发送一包后,等待接收端的响应并完成校验,确认无误后再发送下一包。核心是用TaskCompletionSource把串口的DataReceived事件转换成可等待的异步任务,实现发送-等待校验-再发送的串行流程。
具体实现代码
假设你已经有初始化好的SerialPort实例_serialPort,以及用于校验接收数据的方法VerifyReceivedData,以下是完整实现:
1. 封装单包发送+校验的异步方法
private async Task<bool> SendAndVerifyPacketAsync(byte[] packet) { var tcs = new TaskCompletionSource<bool>(); byte[] receivedBuffer = Array.Empty<byte>(); SerialDataReceivedEventHandler receivedHandler = null; receivedHandler = (sender, e) => { try { if (_serialPort.BytesToRead == 0) return; byte[] tempBuf = new byte[_serialPort.BytesToRead]; _serialPort.Read(tempBuf, 0, tempBuf.Length); receivedBuffer = receivedBuffer.Concat(tempBuf).ToArray(); // 根据你的协议判断是否收到完整一包(必须实现) if (IsCompletePacket(receivedBuffer)) { _serialPort.DataReceived -= receivedHandler; // 校验数据合法性 bool isValid = VerifyReceivedData(receivedBuffer); tcs.SetResult(isValid); } } catch (Exception ex) { _serialPort.DataReceived -= receivedHandler; tcs.SetException(ex); } }; try { _serialPort.DataReceived += receivedHandler; _serialPort.Write(packet, 0, packet.Length); // 设置5秒超时,可根据实际需求调整 return await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); } catch (TimeoutException) { _serialPort.DataReceived -= receivedHandler; Console.WriteLine("接收超时,当前包发送失败"); return false; } } // 必须根据你的通信协议实现:判断是否已接收完整一包 private bool IsCompletePacket(byte[] data) { // 示例1:固定包长协议,假设每包16字节 return data.Length >= 16; // 示例2:带结束符协议(比如\r\n) // return data.Length >=2 && data[^2] == 0x0D && data[^1] == 0x0A; // 示例3:头部带长度字段协议 // if(data.Length < 2) return false; // int packetLength = BitConverter.ToInt16(data, 0); // return data.Length >= packetLength; } // 你的数据校验方法(示例:校验和校验) private bool VerifyReceivedData(byte[] data) { // 假设最后一个字节是校验和 byte calcSum = data.Take(data.Length - 1).Aggregate((a, b) => (byte)(a + b)); return calcSum == data[^1]; }
2. 多包串行发送调用
// 调用示例:传入多包数据列表,串行发送 private async Task ExecuteMultiPacketSend(List<byte[]> packetList) { foreach (var packet in packetList) { Console.WriteLine($"开始发送数据包"); bool sendSuccess = await SendAndVerifyPacketAsync(packet); if (!sendSuccess) { Console.WriteLine("数据包校验失败/超时,终止发送流程"); break; } Console.WriteLine("数据包校验通过,准备发送下一包"); } }
关键注意点
- 粘包/拆包处理:
IsCompletePacket是核心,必须严格匹配你的通信协议,否则会出现接收不完整或粘包导致校验失败的情况 - 超时机制:必须设置超时,避免因串口异常、接收方无响应导致程序挂起
- 事件清理:完成接收或异常时一定要移除
DataReceived事件绑定,防止内存泄漏和重复触发 - 线程安全:串口操作本身不是线程安全的,确保同一时间只有一个发送任务在执行(异步方法本身已经保证串行,所以没问题)
内容的提问来源于stack exchange,提问作者BTOM
相关产品推荐
相关产品推荐

