TCP自定义报头结构体:
/*!
* @brief tcp自定义包头
*/
struct TcpPacketHead{
TcpPacketHead():flag(0X52474B4A)
{
}
uint flag; /*!< 0X52474B4A */
uint index; /*!< 帧计数 */
uint msgType; /*!< 消息类型 */
ushort length; /*!< 数据长度 */
ushort totalLenth; /*!< 数据总长度 */
ushort recvId; /*!< 发送方id */
ushort taskId; /*!< 任务id */
};
需要定义的变量:
/********** tcp接收报文相关 **************/
qint64 bytesReceived; /** tcp当前报文已经接收的大小 */
qint64 recv_totalBytes; /** tcp当前报文需要收到的总大小 */
Datastruct::TcpPacketHead *now_tcpHead;/** 指向当前的要接收的tcp报文头 */
QByteArray now_recv_array;/** 用来存放当前接收到的tcp报文,接收完后才开始解析,长度超出则是下一个报文,取走当前报文后保留 */
变量初始化:(在创建连接的函数中,如connect_server())
bytesReceived = 0; /** 初始化tcp当前报文已经接收的大小为0 */
recv_totalBytes = 0; /** 初始化tcp当前报文需要收到的总大小为0 */
now_tcpHead = NULL;/** 初始化在接收的当前tcp报头为空 */
now_recv_array.clear();/** 清空当前收到的报文 */
更新tcp报文的处理槽
/**
* @brief TcpClient::slotdisconnected
* 请求断开连接后,需要对界面进行的操作
*/
void TcpClient::slotdisconnected()
{
stateTimer->stop();
qDebug()<<QStringLiteral("离开服务器队列");
}
/**
* @brief TcpClient::slot_updateTcpPackage
* 更新tcp接收到的问题,防止收不全
*/
void TcpClient::slot_updateTcpPackage()
{ /** 信号和槽在同一个线程中,不用担心第二次信号来了,会打断当前槽函数 */
bytesReceived +=tcpsocket->bytesAvailable();
QByteArray recv_array = tcpsocket->readAll();
qDebug()<<recv_array.toHex()<<" read all";
now_recv_array.append(recv_array);
if(bytesReceived<sizeof(Datastruct::TcpPacketHead))/** 如果小于报文头部大小,直接返回继续收 */
{
return;
}
if((bytesReceived>sizeof(Datastruct::TcpPacketHead))&&(now_tcpHead == NULL))/** 如果大于报文的大小了,则可以先解析出来一个头,然后判断够不够 */
{
now_tcpHead = (Datastruct::TcpPacketHead *)now_recv_array.data();
recv_totalBytes = now_tcpHead->totalLenth;
if((now_tcpHead->flag)== 0X52474B4A)
{/** 看报文对不对 */
if(bytesReceived <recv_totalBytes)
{
/** 没收完,加进去,继续收 */
return;
}
if(bytesReceived == recv_totalBytes)
{
/** 如果已经够了,就说明收到了一个完整的报文 */
unwarp(now_recv_array); /** 可以解析了 */
bytesReceived = 0;/** 重置接收到的数据字节数 */
recv_totalBytes = 0;/** 下一次其实会覆盖,重置不重置都可以 */
now_recv_array.clear();/** 清空当前接收到的 */
now_tcpHead = NULL;
return;
}
if(bytesReceived > recv_totalBytes)/** 说明有下一次的报文在里面 */
{ qDebug()<<"have next package++++++++";/** 出现沾包了,正在解决 */
QByteArray myarrary = now_recv_array.left(recv_totalBytes);/** 本次报文 */
unwarp(myarrary);
bytesReceived = bytesReceived - recv_totalBytes ;/** 下一次报文已经收到的长度 */
recv_totalBytes = 0;/** 收到的总共的置0,不过下一次解析报头的时候其实会直接覆盖 */
now_tcpHead = NULL;/** 当前报头重新置空 */
now_recv_array = now_recv_array.right(bytesReceived);/** 实际收到的下一次报文 */
slot_updateTcpPackage();/** 手动调用一下,检查是不是这一次剩下的直接够了第二包,避免需要下一次报文才触发检查 */
return;
}
}
else{/** 如果发现有解析不出来的报文了,直接丢弃然后清空当前收到状态,即重置到刚连接的状态 */
qDebug()<<"error TCPhead!!!!!!!";
bytesReceived = 0;/** 重置接收到的数据字节数 */
recv_totalBytes = 0;/** 下一次其实会覆盖,重置不重置都可以 */
now_recv_array.clear();/** 清空当前接收到的 */
now_tcpHead = NULL;
return;
}
}
}
注意前面需要绑定一下信号槽:
connect(tcpsocket, &QTcpSocket::readyRead, this, &TcpClient::slot_updateTcpPackage);
与tcpsocket对象的readyRead信号绑定,可以和前面的变量初始化一起放到创建连接的函数里,如connect_server()