【C#】设备回帧太快、数据没收完整就被读取,导致帧被拆、混、丢,很常见,尤其在高频通信设备,解决方案

像这种,回帧回得太快了,怎么处理?

cpp 复制代码
 private void  Sp_DataReceived(object sender, SerialDataReceivedEventArgs e) {
     this.Invoke((EventHandler)(delegate
     {

         try
         {
             Byte[] ReceivedData = new byte[serialPort.BytesToRead];
             serialPort.Read(ReceivedData, 0, ReceivedData.Length);
             string RecvDataText = "";
             for (int i = 0; i <= ReceivedData.Length - 1; i++)
             {
                 RecvDataText += (ReceivedData[i].ToString("X2"));
             }
             Log($"回帧 : { BitConverter.ToString(ReceivedData).Replace("-", " ")}" );
             // QueryHead.endReceiveData = false;
             return;
         }
         catch (Exception)
         {
             ;
         }
     }));
 }

设备回帧太快、数据没收完整就被读取,导致帧被拆、混、丢,很常见,尤其在高频通信设备中


✅ 问题根源

这段代码:

csharp 复制代码
Byte[] ReceivedData = new byte[serialPort.BytesToRead];
serialPort.Read(ReceivedData, 0, ReceivedData.Length);

💥 会在 每次串口收到部分数据时就立即触发 DataReceived

而不是"整帧都到了才触发"。


🎯 正确思路 ------【接收缓冲 + 延迟判断尾部】

使用一个缓存池接收数据,每次触发 DataReceived 事件时只追加数据不立刻处理 ,

然后设置一个延迟定时器 或 Task.Delay(),如果一段时间内没有再收到新数据,就处理这个缓冲。


✅ 推荐改法(思路 + 代码)

✅ 步骤:

  1. 建一个全局缓冲区 List<byte> _recvBuffer
  2. 在 DataReceived 中追加数据到缓冲区
  3. 启动或重置一个延迟处理器(50ms内不再触发,就认为一帧收完)

✅ 示例代码实现(完整)

csharp 复制代码
private List<byte> _recvBuffer = new List<byte>();
private CancellationTokenSource _recvDelayToken = null;

private void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    int len = serialPort.BytesToRead;
    if (len == 0) return;

    byte[] tempBuf = new byte[len];
    serialPort.Read(tempBuf, 0, len);

    lock (_recvBuffer)
    {
        _recvBuffer.AddRange(tempBuf);
    }

    // 重启延迟处理
    _recvDelayToken?.Cancel(); // 如果已有任务,取消它
    _recvDelayToken = new CancellationTokenSource();
    var token = _recvDelayToken.Token;

    Task.Delay(50, token).ContinueWith(t =>
    {
        if (!t.IsCanceled)
        {
            ProcessFullFrame();
        }
    }, token);
}

✅ 接收完整帧处理方法:

csharp 复制代码
private void ProcessFullFrame()
{
    List<byte> frame;
    lock (_recvBuffer)
    {
        frame = new List<byte>(_recvBuffer);
        _recvBuffer.Clear(); // 清空缓存
    }

    if (frame.Count == 0) return;

    string hex = BitConverter.ToString(frame.ToArray()).Replace("-", " ");
    Invoke(new Action(() => {
        Log($"  回帧完成: {hex}");
    }));

    // TODO: 你可以在这里做校验、解帧等逻辑
}

✅ 优势:

问题 原代码 改进方案
回帧不完整 BytesToRead 不保证收全 缓冲 + 延时处理
多帧合并粘包 多次触发拆掉数据 缓冲中可处理多帧识别
UI卡顿 Invoke 主线程频繁触发 延时合并触发,提高效率

🧪 扩展

: 想调试是否真的收到了完整帧?还可以加 CRC 校验 & 帧头定位,一步步拆多帧组合。


💡建议: 还可以在日志中加上时间戳,方便定位帧间时间问题


🔥 一句话总结:

串口 DataReceived 触发是按"字节"来的,不按"帧"来!必须加缓冲 + 延迟,你才能完整抓住一帧!


相关推荐
十年Java程序媛6 分钟前
JVM 基础与 GC 垃圾回收详解|可达性分析、分代模型、垃圾收集器、STW
java·开发语言·jvm·spring boot·后端·面试
估值探索者7 分钟前
【Python量化选股流水线 #03】候选股怎么排序?动量+低波+流动性三因子打分取 Top-N
开发语言·python·接口·api接口·股票数据接口·数据接口·股票数据api接口
骑士雄师8 分钟前
supervisor 中关于上下文多轮信息维护常用到的python语法
开发语言·python
曹牧17 分钟前
C#:字符串占位符与值数量不一致
开发语言·数据库·c#
迅猛龙办公室25 分钟前
Python函数实现反转一个只有3位数的整数
开发语言·前端·python
“AI国潮设计-小江”28 分钟前
《【AIGC实战】Python+SDXL批量生成“英歌舞树莓香草布丁”IP,附保持角色一致性的代码逻辑》
开发语言·人工智能·python·prompt·aigc
闻缺陷则喜何志丹33 分钟前
【C++特性及类库】多线程
开发语言·c++·多线程·语言特性·系统库
dtq04241 小时前
C++3 - 分支语句if和string变量
开发语言·c++·学习
jimy11 小时前
c++类成员访问只看静态类型
开发语言·c++
156082072191 小时前
银河麒麟 Qt 可执行文件打包说明
开发语言·驱动开发·qt