You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

串口通信连续收发:如何实现逐包校验通过后再发送下一包

串口多包收发串行校验实现方案

核心解决思路

放弃同步循环发送的方式,改用异步串行执行模式:每发送一包后,等待接收端的响应并完成校验,确认无误后再发送下一包。核心是用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 23:15:39