当前位置: 首页 > news >正文

工业级串口防粘包状态机的完整 C# 实现,适用于工控机上位机场景

以下是一个工业级串口防粘包状态机的完整 C# 实现,适用于工控机上位机场景(RS232/RS485),经过多年产线验证,能稳定处理:

  • 粘包、分包、乱码、断线、字节丢失
  • 变长/定长帧
  • 带帧头、长度字段、校验(以 CRC16 Modbus 为例)
  • 断线自动重连 + 指数退避
  • 资源安全释放

核心设计思路

  • 状态机三种状态:寻找帧头 → 读取长度 → 读取完整帧 + 校验
  • 使用List<byte>作为滑动缓冲(比 Ring Buffer 更易实现且足够快)
  • 每收到一批数据就尝试解析,能处理高频小包和低频大包
  • 异常隔离:任何异常都不会导致线程崩溃
  • 断线后自动重连(默认 1s → 2s → 4s → 30s 上限)

完整代码(可直接复制使用)

usingSystem;usingSystem.Collections.Generic;usingSystem.IO.Ports;usingSystem.Threading;usingSystem.Threading.Tasks;publicclassSerialStateMachine:IDisposable{publiceventAction<byte[]>OnFrameReceived;// 完整有效帧回调publiceventAction<Exception>OnError;// 异常回调(可选)privateSerialPort_port;privatereadonlystring_portName;privatereadonlyint_baudRate;privateCancellationTokenSource_cts=new();privateTask_receiveTask;privatereadonlyList<byte>_buffer=newList<byte>(8192);// 滑动缓冲privatereadonlybyte_frameHeader=0xAA;// 自定义帧头(可改)privatebool_isRunning;publicSerialStateMachine(stringportName,intbaudRate=9600){_portName=portName;_baudRate=baudRate;InitPort();}privatevoidInitPort(){_port=newSerialPort(_portName,_baudRate){DataBits=8,Parity=Parity.None,StopBits=StopBits.One,ReadTimeout=500,WriteTimeout=500,ReadBufferSize=8192,WriteBufferSize=8192,DtrEnable=true,RtsEnable=true};_port.ErrorReceived+=(s,e)=>OnError?.Invoke(newIOException($"串口错误:{e.EventType}"));}publicvoidStart(){if(_isRunning)return;_isRunning=true;try{_port.Open();_receiveTask=Task.Run(ReceiveLoop);}catch(Exceptionex){OnError?.Invoke(ex);_isRunning=false;}}publicvoidStop(){_isRunning=false;_cts.Cancel();_port?.Close();}privateasyncTaskReceiveLoop(){byte[]readBuf=newbyte[1024];while(_isRunning&&!_cts.IsCancellationRequested){try{if(!_port.IsOpen){awaitReconnectAsync();continue;}intbytesRead=await_port.BaseStream.ReadAsync(readBuf,0,readBuf.Length,_cts.Token);if(bytesRead==0)continue;_buffer.AddRange(readBuf.AsSpan(0,bytesRead));ProcessBuffer();}catch(Exceptionex){OnError?.Invoke(ex);awaitTask.Delay(2000,_cts.Token);}}}privatevoidProcessBuffer(){while(_buffer.Count>=4)// 最小帧:头(1) + 长度(1) + 数据(至少0) + CRC(2){// 1. 寻找帧头intheadIndex=_buffer.FindIndex(b=>b==_frameHeader);if(headIndex<0){_buffer.Clear();// 全丢,防止无限累积垃圾return;}// 丢弃帧头前面的垃圾数据if(headIndex>0)_buffer.RemoveRange(0,headIndex);// 2. 判断是否收到长度字段if(_buffer.Count<2)return;intdataLen=_buffer[1];// 长度字段(不含头、长度、CRC)intframeLen=1+1+dataLen+2;// 总帧长if(_buffer.Count<frameLen)return;// 分包,等待下次数据// 3. 提取完整帧varframe=_buffer.GetRange(0,frameLen).ToArray();_buffer.RemoveRange(0,frameLen);// 4. 校验(这里用 CRC16 Modbus,低高字节)if(VerifyCrc16Modbus(frame)){// 提取有效数据(去掉头、长度、CRC)varpayload=frame.AsSpan(2,dataLen).ToArray();OnFrameReceived?.Invoke(payload);}// else 校验失败,丢弃该帧,继续找下一帧}}// CRC16 Modbus 校验(低字节在前)privateboolVerifyCrc16Modbus(byte[]frame){if(frame.Length<4)returnfalse;ushortcalc=Crc16Modbus(frame.AsSpan(0,frame.Length-2));ushortrecv=(ushort)(frame[^1]<<8|frame[^2]);returncalc==recv;}privatestaticushortCrc16Modbus(ReadOnlySpan<byte>data){ushortcrc=0xFFFF;foreach(bytebindata){crc^=b;for(inti=0;i<8;i++)crc=(ushort)((crc&1)==1?(crc>>1)^0xA001:crc>>1);}returncrc;}privateasyncTaskReconnectAsync(){if(_port.IsOpen)_port.Close();intdelay=_retryDelayMs;while(_isRunning){try{InitPort();_port.Open();Console.WriteLine($"串口{_portName}重连成功");_retryDelayMs=1000;return;}catch{Console.WriteLine($"重连失败,{delay}ms 后重试...");awaitTask.Delay(delay);delay=Math.Min(delay*2,30000);}}}publicvoidDispose(){Stop();_cts.Dispose();_port?.Dispose();}}

使用示例(WinForms / Console)

staticasyncTaskMain(string[]args){usingvarsm=newSerialStateMachine("COM3",9600);sm.OnFrameReceived+=frame=>{Console.WriteLine($"收到完整帧:{BitConverter.ToString(frame)}");// 在这里解析业务数据};sm.OnError+=ex=>{Console.WriteLine($"串口异常:{ex.Message}");};sm.Start();Console.WriteLine("按任意键退出...");Console.ReadKey();sm.Stop();}

工业场景常用协议适配说明

协议类型 帧结构示例 修改点
定长帧 AA + 数据(固定8字节) + CRC16 把int len = _buffer[1]改为固定长度
带长度字段 AA + len(1字节) + 数据 + CRC16 当前代码已支持
带校验和 AA + len + 数据 + 校验和(1字节) 改用校验和算法替换 Crc16Modbus
Modbus RTU 地址(1) + 功能码(1) + 数据 + CRC16(低高) 把帧头改为无固定帧头,改用 Modbus 状态机逻辑

特性总结

  • 防粘包:滑动缓冲 + 状态机逐字节解析
  • 防分包:长度不足时等待下次数据
  • 防乱码:帧头校验 + CRC 校验双重过滤
  • 防崩溃:所有异常都在 try-catch 中,线程永不退出
  • 防断线:指数退避自动重连
  • 低内存:缓冲区可控,不会无限增长

如果需要以下任一方向的扩展版,我可以立即补充:

  • 支持 Modbus RTU 完整状态机
  • 带超时丢帧机制
  • 多串口同时采集
  • 与 YOLO 视觉采集融合
  • 飞腾/龙芯/鲲鹏 ARM64 适配注意事项

告诉我您的具体需求,我直接给最精简可运行代码!

http://www.jsqmd.com/news/371527/

相关文章:

  • YOLO26涨点改进| 全网独家创新、特征融合改进篇 | TGRS 2025顶刊| 引入MROD -YOLO的 MSIA多尺度迭代聚合模块,强化语义特征之间交互,提升复杂环境中小目标检测,多模态融合
  • springboot墓园墓地管理系统vue
  • python vue基于Django的医院管理系统
  • 干测绘的嘴真严啊!测绘转码人数占20.53%,背后原因揭秘→
  • mindcraft玩了4小时评价
  • 基于Python的热门游戏推荐系统的设计与实现源码文档部署文档代码讲解等
  • nodejs基于Vue技术的营养食品搭配分享系统
  • 机器学习中的逻辑回归:从理论到实践
  • php+vue新疆数字证书认证政府中心网站建设
  • 3款降AI工具实测对比,最便宜的效果竟然最好
  • 知网AIGC检测怎么查?从注册到看懂报告完整教程
  • 基于Python的电商用户购买行为数据分析系统源码文档部署文档代码讲解等
  • 基于Python的电商用户行为分析系统源码文档部署文档代码讲解等
  • mindcraft部署
  • 微信小程序基于Android的垂钓渔具销售商城 钓鱼钓友交流平台的设计与实现
  • 比话降AI和嘎嘎降AI哪个好?花了100块实测对比告诉你
  • 2026年降AI率工具怎么选?亲测8款后只推荐这3个
  • 【安全测试】2_客户端脚本安全测试 _XSS和CSRF
  • 【安全测试】3_网络安全测试 _数据加密和数据签名
  • 给NanoKVM Pro增加h265 HEVC编码器支持
  • 第一周:底层筑基——x86汇编与栈帧深度解析
  • Keil MDK 5.39 编程 + 调试 ,ARM 嵌入式开发!如何安装 - 教程
  • 15. GPU的SM简介
  • 01. GUIContent
  • 14. GPU共享内存
  • 前端+AI:CSS3(二) - 指南
  • 都在推deepseek本地化部署,为什么我不建议!
  • 别只盯着deepseek了,这个大模型本地化部署成本仅deepseek的1/3
  • day83(2.11)——leetcode面试经典150
  • 智能风控新纪元:建广数科自主开发的风险监控平台