引言
在udp通信中,发送请求后,等待接收端的响应,有时一个回应被分割成好几次发送,这种反馈的形式出现带有随机性,正常的情况下是一次反馈所有的数据,但是会出现一次反馈被分成好几次反馈。像下面这样:
请求:<history>
成功响应:<1,history,arg=78,total=1794,students=23><2,history,arg=65,total=1300,students=20><3,history,arg=73,total=1600,students=22>
<history,ok>
失败响应:<history,error>
发送请求后,接收端会返回给发送端所有的班级历史成绩的信息,本应该一次性返回,但是若随机出现接收端将要返回的数据分成好几次返回给发送端。像这样:
请求:<history>
响应:<1,history,arg=78,total=1794,students=23>
发送端接收到的数据并没有一次性返回,而是先接收一部分返回的数据,过一会儿后又接收到接收端返回的数据:
<2,history,arg=65,total=1300,students=20><3,history,arg=73,total=1600,students=22>
再过一会儿才接收到接收端返回的最后一句:
<history,ok>
上面描述的这种情况,本文记录的处理数据的方式,就是针对于上面所述的不能一次性返回一个响应给发送端。
方案
针对上面不能一次性返回一个响应的情况,下面来说一下如何接收这样的数据,使数据完整后再针对于上面的响应的格式来处理数据。
udp中常规接收数据的方式
while (m_udp->hasPendingDatagrams()) {QByteArray byteArray;byteArray.resize(m_udp->pendingDatagramSize());QHostAddress ip;quint16 port;qint64 ret = m_udp->readDatagram(byteArray.data(),m_udp->pendingDatagramSize(),&ip,&port);qDebug()<<QStringLiteral("接收的数据:")<<byteArray.data()<<QStringLiteral("大小:")<<ret;}
上述不能一次性接收返回数据的情况不能用udp中常规接收数据的方式来接收,这样接收到的是一部分一部分的,需要再在数据处理里改动,为了使数据处理按照本文开始时给出的响应格式来解析,不改动数据处理部分,现在来调整接收数据的代码,使传送到数据处理时是一条完整的回复,当然必须满足响应数据一次性全部返回和响应数据一部分一部分的返回两种情况。
处理思路
如果是一次性返回响应的数据,直接传到数据解析函数中进行数据解析。
如果是一部分一部分的返回响应的数据,就将每一次返回的数据进行追加,构成一个字符串,直到接收到响应数据中含有<history,ok>或<history,error>将所有追加狗长城的字符串传入数据解析函数中。
处理实现
因为要数据进行追加成一个字符串,所以就需保存上一次的数据,将下一次接收的数据追击哀悼上一次数据的后面,直到最后接收到的数据中含<history,ok>或<history,error>。
QByteArray msgByteArray;//返回的数据是分开发送的(逐条发送),所以累计接收while (m_udp->hasPendingDatagrams()) {QByteArray byteArray;byteArray.resize(m_udp->pendingDatagramSize());QHostAddress ip;quint16 port;qint64 ret = m_udp->readDatagram(byteArray.data(),m_udp->pendingDatagramSize(),&ip,&port);msgByteArray.append(byteArray);}QString strTemp = msgByteArray;//临时保存接收的数据if (!strTemp.contains("ok")) {//是否接收数据中含有okif (strTemp.contains("error")) {//请求失败qDebug()<<QStringLiteral("接收的字节数:")<<msgByteArray.size()<<QStringLiteral("数据:")<<msgByteArray.data();m_interface->onReceviedData(msgByteArray);//将接收的一次完整的响应回调给数据处理函数}else {//只回应了一部分数据,后面还有数据//保存此次接收的数据,等到结束标志<history,ok>到来的时候,与接收标志的数据叠加m_partByteArray = msgByteArray;//保存每一次接收的数据,并追加后面收到的数据}}else {if (!m_partByteArray.isEmpty()) {m_partByteArray.append(msgByteArray);msgByteArray.clear();msgByteArray = m_partByteArray;//为了使发送的数据依旧是msgByteArray,同时满足当返回响应是失败m_partByteArray.clear();}//直到接收到一次完整的数据qDebug()<<QStringLiteral("接收的字节数:")<<msgByteArray.size()<<QStringLiteral("数据:")<<msgByteArray.data();m_interface->onReceviedData(msgByteArray);}
上面这段代码的逻辑,既能满足一次性接收完整的响应(成功,失败的响应),也能满足一部分一部分的接收一次响应。 m_interface->onReceviedData(msgByteArray);是将接收到的一次完整的响应数据已回调的方式传递到数据处理函数中。
领悟
在qt下udp通信中数据接收的方式,还是要根据自己项目的需求来做适当的修改,并不是是常规接收数据那样一成不变的。