QT Udp/Tcp 收发 自定义复杂数据包【包头+内容】(key

您所在的位置:网站首页 udp报文内容 QT Udp/Tcp 收发 自定义复杂数据包【包头+内容】(key

QT Udp/Tcp 收发 自定义复杂数据包【包头+内容】(key

2023-11-09 07:12| 来源: 网络整理| 查看: 265

      在学习QT的过程,客户端/服务端 收发自定义复杂数据包(结构体带指针的不定长数据)的处理             示例如下,首先在头文件中申明这个自定义结构体(收发部分都需要包含这个结构体定义)        其中char pValue[0]表示这个是变长的部分!!! #pragma pack(1) struct DHPacket { int len; char pValue[0]; }; #pragma pack()     一定要记得#pragma pack(1)【copy 一段别人的解释 :)   (在网络协议编程中,经常会处理不同协议的数据报文。一种方法是通过指针偏移的方法来得到各种信息,但这样做不仅编程复杂,而且一旦协议有变化,程序修改起来也比较麻烦。在了解了编译器对结构空间的分配原则之后,我们完全可以利用这一特性定义自己的协议结构,通过访问结构的成员来获取各种信息。这样做,不仅简化了编程,而且即使协议发生变化,我们也只需修改协议结构的定义即可,其它程序无需修改,省时省力。)】

 

    在发送端,组织代码,进行包装,即

  DHPacket* dhPacket = new DHPacket();   dhPacket->len = len;//把真实的数据长度放在这个dhPacket的成员变量len里(PS:这个真实数据指的是实际放在char pValue[0]里面的内容)

   接着申请长度为这个sizeof(DHPacket)+len的char* allData数据,

   并将其前【0~sizeof(DHPacket)-1】的内容填充为dhPacket的值;

   接着将其【sizeof(DHPacket)~sizeof(DHPacket)+len-1】的值填充为真实的数据

   这样就可以将其系列化的数据发送出去

QString info; info = lineEdit->text(); info += "\0"; if(info.length() > 0) {//有数据 //自己定义DHPacket 组装发送给客户端 const char* data = info.toStdString().data(); int len = strlen(data); int packetSize = sizeof (DHPacket); DHPacket* dhPacket = new DHPacket(); dhPacket->len = len; char* allData = new char[sizeof(DHPacket)+len]; memcpy(allData,(char*)dhPacket,sizeof(DHPacket)); memcpy(allData+sizeof (DHPacket),data,len); if(udpSocket->writeDatagram( allData,sizeof(DHPacket)+len,QHostAddress::Broadcast,portID) != sizeof(DHPacket)+len) { QMessageBox::information(this,"提示", QString("%1 广播失败").arg(info)); } free(allData); }

 

   接着在接收端,来解析收到的数据包。

     首先将所有的数据先接受下来:即

int allDataLen = clientSocket->pendingDatagramSize(); char* alldata = new char[allDataLen];   clientSocket->readDatagram((char*)alldata,allDataLen);

所有的数据就放在alldata里面了,

接着获取DHPacket的信息,并得到真实数据的长度    

DHPacket* dhPacket = new DHPacket; memcpy(dhPacket,alldata,sizeof (DHPacket));

接着再申请一个真实数据长度+1的字符指针,并将其最后一位填充为‘\0’(即字符串结束符)【如果最后这个真实数据不是拿去显示字符串的,这里这个真实数据长度不用+1 去申请】

void UdpClient::ReceiveData() { //自己定义DHPacket 从服务器端收数据 if(clientSocket->hasPendingDatagrams()) { int allDataLen = clientSocket->pendingDatagramSize(); char* alldata = new char[allDataLen]; clientSocket->readDatagram((char*)alldata,allDataLen); DHPacket* dhPacket = new DHPacket; memcpy(dhPacket,alldata,sizeof (DHPacket)); int realDataLen = dhPacket->len;//获取结构体中标志实际数据的长度 if(realDataLen >= 0) { char* realData = new char[realDataLen+1];//长度+1,给其最后一位给'\0'字符串结束符 memcpy(realData,alldata+sizeof(DHPacket),realDataLen); realData[realDataLen] = '\0';//字符串加上最后一位结束符 QString recevie(realData); recevie += "\n"; recevieTE->insertPlainText(recevie); } if(alldata != nullptr) delete []alldata; } }

 

最后效果如下,完美发送自定义结构体(含有指针的)内容的收发 :)

 

 

对于更复杂的数据包,更常用的我也实现了,后面附上关键代码 首先包的定义(包头+内容) #pragma pack(1) struct DHPacket { int len; char pValue[0]; }; enum enPacketType { PACKET_CONNECT = 0, PACKET_CONTENT, PACKET_BIG_CONTENT, PACKET_HEART_BEAT,//心跳包 只对TCP进行 }; struct DHPacketHead { int type;//1:PacketConnect 2:PacketContent int len; char pValue[0]; }; struct DHPacketHeadEx { enPacketType packetType; int len; char pValue[0]; DHPacketHeadEx() { packetType = enPacketType::PACKET_CONNECT; } }; struct PacketConnect { bool isConnect; }; struct PacketContent { char contents[1024]; }; struct PacketBigContent { char ip[1024]; char name[1024]; PacketBigContent() { memset(ip,0,sizeof(ip)); memset(name,0,sizeof(name)); } }; struct PacketHeartBeat {//心跳包 就弄个空包 }; #pragma pack()            发送部分代码如下: void UdpServer::SendConnect() { static bool isConnect = true; char *bufs = new char[sizeof(DHPacketHead) + sizeof(PacketConnect)]; DHPacketHead* dhPacket = (DHPacketHead*)bufs; dhPacket->type = 1; dhPacket->len = sizeof(PacketConnect); PacketConnect* packetConnet = (PacketConnect*)dhPacket->pValue; packetConnet->isConnect = isConnect; isConnect = !isConnect; if(udpSocket->writeDatagram( bufs,sizeof(DHPacketHead) + sizeof(PacketConnect),QHostAddress::Broadcast,portID) != sizeof(DHPacketHead) + sizeof(PacketConnect)) { QMessageBox::information(this,"提示", QString("%1 广播失败").arg("PacketConnect")); } } void UdpServer::SendContent() { QString info; info = lineEdit->text(); const char* data = info.toStdString().data(); char *bufs = new char[sizeof(DHPacketHead) + sizeof(PacketContent)]; DHPacketHead* dhPacket = (DHPacketHead*)bufs; dhPacket->type = 2; dhPacket->len = sizeof(PacketContent); PacketContent* packetContent = (PacketContent*)dhPacket->pValue; strcpy(packetContent->contents,data); if(udpSocket->writeDatagram( bufs,sizeof(DHPacketHead) + sizeof(PacketContent),QHostAddress::Broadcast,portID) != sizeof(DHPacketHead) + sizeof(PacketContent)) { QMessageBox::information(this,"提示", QString("%1 广播失败").arg("PacketContent")); } delete [] bufs; } void UdpServer::SendBigContent() { char* sendData = new char[sizeof (DHPacketHeadEx) + sizeof(PacketBigContent)]; DHPacketHeadEx* headEx = (DHPacketHeadEx*)sendData; headEx->len = sizeof(sizeof(PacketBigContent)); headEx->packetType = enPacketType::PACKET_BIG_CONTENT; PacketBigContent* bigContent = (PacketBigContent*)headEx->pValue; QString localHostName = QHostInfo::localHostName(); QHostInfo hostInfo = QHostInfo::fromName(localHostName); //获取主机IP地址 QList listAddr = hostInfo.addresses(); QString allAddrs; for(int index;index < listAddr.length();index++) { QHostAddress perAddr = listAddr.at(index); QString addr = perAddr.toString(); addr += "\t"; allAddrs += addr; } allAddrs+="\t"; memcpy(bigContent->ip,allAddrs.toStdString().data(), strlen(allAddrs.toStdString().data())); memcpy(bigContent->name,localHostName.toStdString().data(), strlen(localHostName.toStdString().data())); if(udpSocket->writeDatagram( (char*)headEx,sizeof (DHPacketHeadEx) + sizeof(PacketBigContent),QHostAddress::Broadcast,portID) != sizeof (DHPacketHeadEx) + sizeof(PacketBigContent)) { QMessageBox::information(this,"提示", QString("%1 广播失败").arg("PacketContent")); } else { QMessageBox::information(this,"提示", QString("发送成功 IP:%1\r\nname:%2").arg(allAddrs).arg(localHostName)); } delete [] sendData; } void UdpServer::SendHeartBeat() { char* sendData = new char[sizeof (DHPacketHeadEx) + sizeof(PacketHeartBeat)]; DHPacketHeadEx* headEx = (DHPacketHeadEx*)sendData; headEx->len = sizeof(sizeof(PacketBigContent)); headEx->packetType = enPacketType::PACKET_HEART_BEAT; if(udpSocket->writeDatagram( (char*)headEx,sizeof (DHPacketHeadEx) + sizeof(PacketHeartBeat),QHostAddress::Broadcast,portID) != sizeof (DHPacketHeadEx) + sizeof(PacketHeartBeat)) { QMessageBox::information(this,"提示", QString("%1 广播失败").arg("心跳包")); } else { QMessageBox::information(this,"提示", QString("发送成功 心跳包")); } delete [] sendData; }

 

接收部分如下 void UdpClient::ReceiveDataEx() { //自己定义DHPacketHeadEx+内容 从服务器端收数据 if(clientSocket->hasPendingDatagrams()) { int allDataLen = clientSocket->pendingDatagramSize(); char* alldata = new char[allDataLen]; clientSocket->readDatagram((char*)alldata,allDataLen); if(allDataLen > sizeof (DHPacketHeadEx)) {//长度才正常 DHPacketHeadEx* head = (DHPacketHeadEx*)alldata; switch(head->packetType) { case enPacketType::PACKET_BIG_CONTENT: //服务器端发的大报文 { PacketBigContent* pData = (PacketBigContent*)head->pValue; QString strInfo = QString("服务器发来连接内容\r\nip:%1 \t name= %2").arg(QString(pData->ip)).arg(QString(pData->name)); strInfo+="\n"; recevieTE->insertPlainText(strInfo); } break; case enPacketType::PACKET_HEART_BEAT: { QString strInfo = QString("服务器发来心跳包"); strInfo+="\n"; recevieTE->insertPlainText(strInfo); } break; case enPacketType::PACKET_CONNECT: break; case enPacketType::PACKET_CONTENT: break; default: //错误的报文 break; } } } }

 

最后效果如下图:

 

 

 

后面附上三张图,讲述实际组织数据包收发

 

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3