IO424使用Socket收发Modbus协议格式数据时发生粘包问题的解决办法。

本文于2021年8月6日首发于博客园,博文已同步至作者微信公众号,转载请注明。

最近参与了公司的一个项目,使用山东某公司的IO424产品(作者未通过本文获取任何商业和非商业收益,如有侵权,请联系我删除)来做数据交互。

项目大概的需求是:当传感器感应到产品,或者感应不到产品(即上升沿和下降沿)时,会使IO424的数据输入(DI口)发生变化,并将此变化发送出去。

IO424一共有4个DO口,4个DI口。我要从IO424发来的数据中,分别判断4个DI口的状态。由于之前对Modbus协议不是特别的理解,也没有遇到过数据粘

包的问题,导致这次项目的进展有所拖延,经过一周时间的突击学习Modbus,以及数据粘包问题,解决了问题,幸而没有造成太大的影响,期间在与售后

的交流中,学习到不少产品以外的知识,感谢他们的真诚和耐心帮助。

 

咱们现在进入正题: 我遇到的第一个问题是,如何解析IO424发来的数据。第二个问题是,发来的数据格式有时会被打乱,无法解析。

那么下面就开始解决问题,在以后的内容中,将不再提及Modbus这个词,因为对于不了解Modbus的朋友,反而会造成干扰。

为了使本文更具有通用性,IO424一律使用“设备”一词来代替。

一,如何理解设备发来的数据

       1, 按照项目的实际需求,我在设备上开启了主动上报功能(有上升沿或者下降沿时,设备会将数据发送到上位机)。本项目使用网口来收发数据。

         某次通信,我收到了以下数据:11-42-00-20-00-08-02-01-00-EA-19。含有EA这样的字眼,说明他是16进制的,共11个字节,通过阅读说明书,

     以及咨询技术售后和查资料等,我大概理解了这串数据的意思:

        11:设备号,在组网时用于区分设备。

      42:功能码,不是本文重点,先不用管他是什么。

    00-20:代表设备上的一段硬件地址,您需要有简单的单片机或者PLC知识。

         00-08:表示寄存器单元长度,您需要有简单的单片机或者PLC知识。

         02:字节数。

         01-00:收到的数据。

    EA-19:CRC校验数据。

       2,我从Socket的缓存中读取11个字节(11这个数字很重要,后面还会使用),就会得到这串数据。那如果我读12字节呢?就会得到

    11-42-00-20-00-08-02-01-00-EA-19-00这样的数据。如果读13个字节呢?就会得到11-42-00-20-00-08-02-01-00-EA-19-00-00,除

    去这有意义的11个字节,其他读到的都是0,都是无意义的空值,所以写程序的时候,读取长度为11就足够了,再多了读取到的都是

    无意义的0(您应当对Socket的缓存有一定的认识)。其中对我们有意义的,就是01-00这个数据了。

  3,刚才说了,他是16进制的,用Windos系统自带的程序员计算器计算一下,0100的十进制是256,256这个数字怎么和4个DI口的状态对

    应上呢?说明书上没写!我对着屏幕一头雾水,不知道该怎么办。对着屏幕沉默了半天,不知道该怎么办。我把产品反复的放进不同的

    DI口,分析我收到的每一组数据。当我把产品放进DI2,我收到的数据是11-42-00-20-00-08-02-03-00-EB-79,有意义的数据是03-00,

    他的十进制是768,768又怎么和4个DI口对应上呢?说明书上没写!我对着屏幕一头雾水,不知道该怎么办。对着屏幕沉默了半天,不

    知道该怎么办。

   4,后来无意中,我想起来售后的一句话,CRC校验时,高低位要对调一下。我试着把01-00这个数据对调一下,变成了0001,把03-00对

    调一下,变成0003。0001对应的十进制是1,0003对应的十进制是3,这个数字怎么和4个DI口的状态对应上呢?对着屏幕沉默了半天,不

    知道该怎么办。

     5,后来,就在盯着屏幕发呆的时候,我注意到了程序员计算器中的二进制,1的二进制是0001,3的二进制是0011,刚好是四位,是不是就对

    应着DI的4个口的状态呢?经过我反复的取放测试,果然是这个样子,这中间还有一个弯。比如0011,他依次对应的是DI4,DI3,DI3,DI1四个口

    的状态,0表示没有产品,1表示有产品。0011意思就是,DI4,DI3没有产品,DI2,DI1有产品。

   6,至此,已经完全的解析出了设备发来的数据,回想一下学习的过程,简直如同是在破案。学艺不精,影响项目进度,惭愧。

 

二,下面说一下数据粘包的问题,以及是怎么解决的。

   1,按照上一步对设备发来数据的理解,编好了程序,先跑起来看看撒

     

   2,果然,还是出问题了。有一天,我收到了这样一组数据:

             

             20-00-08-02-01-00-EA-19-00-00-00。这是什么个意思,是程序有问题,还是设备有问题?沉思了好久,突然想到,这是不是就是传说中的

    粘包啊?当初学习Socket的时候,看见过这个词,但是一直没遇到过。后来想了想,由于我在设备上也开启了DO口上报,并且DO口上报非

      常的频繁,可能在300毫秒以内,估计就是导致了粘包,两次产生的数据合并在一起发送,就出问题了。

   3,发现了问题,怎么解决呢?对着屏幕上的一堆数据又是一顿沉默。观察了好久,终于想起来,这些数据都是有规律的。例如,我只需要DI上报,

    而DI上报,是有固定的长度(11位)和格式的。抓住这个特点,我又修改了一下程序,变成了这样:

              

            简单说来,就是先尝试在Socket缓存读取3个字节,如果这三个字节是11-42-00,那么我就确定他是来自于DI上报的数据,接着再去缓存读取8个字节,

     3+8=11,刚好就把这次DI上报的数据读完了。这11个字节,就是DI上报的一个完整的数据。就不会再发生粘包了。

  4,以前学习Socket,大都会看到一句话:byte[] head = new byte[1024],为什么是1024,而不是1023或者1025呢?大都没有说明具体的含义是什么。

             那么接下来,我就按照我的理解,以及测试结果,大概的说一下我的理解吧。

 

三, Socket中的缓存,以及byte[] head = new byte[1024]的理解。

             1, 我们从收发数据讲起(再底层的我也不懂了):

         每当我们建立起一个有效的Socket连接,系统就会给这个Socket实例分配一个存储空间,这个空间大小有个默认值,不同的操作系统是不一样的,当然,

     我们也可以重新指定这个存储空间的大小。这个存储空间,就是我们通常所说的缓存。他会接收另一端Socket发来的数据,并且一直存储,直到主动去到

     这个缓存去读取数据,或者Socket失效,否则,缓存区的数据就会一直存在(存疑,没有验证过数据可以保存多久)。

    2,那么byte[] head = new byte[1024]这句话到底什么意思呢?为什么不是1023或者1025呢?其实他的意思是:本次我想从缓存区读取1024个字节。 

              3,那么问题又来了,当我从缓存区读取1024个字节之后,缓存区发生了什么?不理解这个,就很难解决粘包的问题。

    4,下面我根据自己的理解,画个简单的示意图(红色字体为缓存区长度,黑色和蓝色字体数为接收到的数据):

      

      我们假定本次建立的Socket实例,系统给我我们分配了24个字节长度的缓存空间。并且,另一端的Socket已经给我们发来了数据:

      255,2,1,102,200,102,89。如果我不主动去读取,这些数据就会一直保存在缓存区,直到缓存区被后来的数据塞满(缓存区)

                      被塞满之后,系统该如何处理已经存在数据,以及如何处理正在发来的数据呢?欢迎大家继续研究验证)。下面,我们来读取缓存区中的

     数据。先读取3个字节吧,byte[] head = new byte[3],读取完之后,缓存区的数据发生了什么变化呢?他变成了这样:

      

                发生了什么?通过对比,我们知道,255,2,1这3个字节没有了,102,200,102,89被向前移动了,后面的全部补0。如果此后另一端的Socket

     再发来数据,缓存区的数据,就变成了下面这个样子:

     

               其中蓝色字节,就是另一端的Socket再次发来的数据。那我们在编程的时候,就不能再去读大于4个字节长度的数据了,如果我们尝试读取大于4个字

    节长度的数据,这将发生粘包。

    5,所以解决本次粘包的问题,我是先尝试读取3个字节,如果是11-42-00,我再继续读取8个字节,一共是11个字节,构成一个完整的数据包。缓存区数据一

     旦被读取,他们就不存在了,后面的数据会自动往前填充(当然,根据各个框架下的API,您也可以使用补偿,偏移等方式,来读取指定位置的字节,

     指定的字节被读取以后,不知道缓存区的数据变成了什么样子,有心的朋友可以做个验证)。

    6,由于本次项目,是购买的成品设备,无法自定协议来解决粘包问题。当我们在项目中遇到Sokcet收发数据高并发时,可以自定协议,来解决。

     

 

电脑查看不方便?试试关注公众号吧,本博文已同步至微信公众号。

 

posted @ 2021-08-06 16:02  zch半缘修道半缘君  阅读(1538)  评论(1)    收藏  举报