串口中数据无法准确接收完全是做串口相关开发工作时经常遇到的问题,每当遇到断包、粘包是特别头疼,最近在做一个串口对接的程序时为此纠结很久,感谢Mr.王提供的思路节省了很多优化时间。
1. 串口开发时如果你使用的协议是类似如下格式你的开发可能会轻松些:
帧头+长度+功能码+数据+校验码+帧尾
这个时候做以下操作基本可以解决问题
1)校验是否有帧头、帧尾,或者校验长度、校验码是否正确
2)如果1)不正确,需要判断是断包还是粘包,
断包需要:1. 如果缓存中有断包数据,尝试是否可以组成整包;2. 如果缓存中没有数据、或者仍未组成整包数据,继续缓存该断数据。
粘包需要:1. 将包先拆开,每个包需要校验完整性后依据断包来处理。
2. 如果你的协议里面包括:
帧头+长度
1)这只能校验帧头+长度了,发现长度不够时,把已经接收的数据缓存起来匹配接下来接收的数据再次校验长度,如果只接收到帧头,下个包第一个字节就是长度。
3. 如果你的协议里没有固定的协议格式,这就很苦逼了,发送数据的格式和长度都不是固定的怎么接收?Mr.王给我的方法如下:
1)传统方式每隔一段时间取一些数据,这个‘一段时间’根据程序之间最大包大小、串口波特率和收发包频繁间隔来考虑,一般100ms可以。
2)收到包时先不接收,保存当前可读数据长度,再等待大概一小段时间查询可读数据长度,这个‘一小段时间’根据程序之间最大包大小、串口波特率和收发包频繁间隔来考虑,一般10ms可以。如果发现两次可读数据长度不一致说明数据未接收完成,继续等待,直至两次获取的可读数据长度一致取出数据。
关于串口通讯每帧时间计算,摘自 https://www.jianshu.com/p/bb0ab9a58eff
while (true)
{
try
{
if (serialPort.IsOpen )
{
int n = serialPort.BytesToRead;
if (n > 0)
{
while(true){
Thread.Sleep(10);
int m = serialPort.BytesToRead;
byte[] buf = null;
if (m > n)
{
n = m;
continue;
}
buf = new byte[m];
serialPort.Read(buf, 0, m);
break;
}
}
}
Thread.Sleep(100);
}
catch (Exception ex)
{
}
}
仅记录开发中解决问题的思路,发现新的有效的方法时再更新该文章。