一、背景
在光纤项目中,作为查看信号实时波形、配置采集卡等功能的本地客户端,实现该客户端过程中,出现数据接受不全,运行时间久了会出现信号延迟等问题,达不到波形实时的效果。
二、实现方式
1、 本地客户端是通过服务器不断的获取数据,然后协议封装之后等到数据。
2、 客户端这边通过Qt中的类QTcpSocket来接受服务端的数据。即信号和槽:
connect(m_pSocket, SIGNAL(readyRead()), this, SLOT(receiveData()));
想法是采集卡每获取一组数据中间会有几十毫秒的间隔,这边的信号也就有实时触发并接收数据。
三、原因分析
客户端接受函数用的是Qt类中封装的readAll()函数,当初的想法是服务端有多少数据发送过来,客户端就全部读取。出现数据接受不全的问题会不会是服务端发送的数据太大,函数readAll()一下子接受不过来,readAll()官方文档介绍如下:
官方文档介绍说一下子接受所有数据,又查看了源码
循环读取接受,想的是会不会这个MaxByteArraySize较小
很明显不是reaAll的问题。
换个思路,因为不改变采集卡参数的情况下,每组数据大小是固定的,于是就将接收函数换成read(qint64
maxSize)读取,这就造成了运行时间长了,波形不实时的问题,也造成了我感觉是画图造作时间大于每组发送数据时间的假象。
重回起点,问题根源应该不在接收函数这里,那就应该是信号不及时触发,于是写了个测试代码,服务端循环发送,每次发很小的数据,客户端信号触发次数不对等,也就是服务端发了三次,客户端这边可能一次触发将三次数据全部接受。
之前的误解就是发送端wirte一次,readyread就会触发一次,上面的测试例子就是几组数据readyread触发一次,write一次大数据量的数据,readyread会触发多次,简单来说就是符合粘包分包机制。
接着往下深究,对于tcp,数据是连在一起的分不开的,最小粒度是1字节。发送端的应用程序write函数一次写了多少数据,接收方的系统是不知道的。那么接收方系统到底接收到多少数据才发送给qt应用程序呢?这个算法是这样的:超时和超过缓冲。超时:无论来了多少数据,超过这个时间,系统就会发送当前接收到的数据给qt应用程序。超过缓冲,在未超时的情况下,系统缓冲区满了,系统就会将数据发送给qt应用程序。系统TCP/IP缓冲最大是65536个字节。
四、解决方案:
知道了信号触发的机制,在槽函数中用哪种读取数据函数就不纠结了,这里还是采用的是readAll(),一下全部读掉。代码如下:
int OneFrameDataLen = 1000; //没帧数据大小
Buffer += m_pSocket->readAll(); //一次接受全部数据
ReceiveLen = Buffer.size(); //接受的数据大小
if(ReceiveLen < OneFrameDataLen) //分包处理,数据不够接续接收
{
return;
}
//接受的数据大于等与一包的数据大小,粘包处理
while(1)
{
ReceiveBuf = Buffer.mid(0, OneFrameDataLen); //截取获取一完整包的数据
Buffer=Buffer.mid(OneFrameDataLen, ReceiveLen - OneFrameDataLen); //获取多余
Do something; //处理数据函数
ReceiveLen = Buffer.size(); //剩下数据大小
if(ReceiveLen< OneFrameDataLen) //不满一包数据退出,等待下次信号触发
{
break;
}
}
五、经验总结
本文介绍了QtTcpSocket数据接受不全,数据不实时的问题,分享了在实际项目的解决问题的方案和遇到问题的分析,为后具有相同问题的解决提供一定的参考。
在此问题中,作者分析问题时走了一定的弯路,第一时间没有抓住问题的根源所在,通过本文,作者分享在开发过程中的一些思考和解决方案,并不代表最佳方案和实践,仅供参考。