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

本文介绍了如何在QT中使用UDP进行自定义结构体数据包(包含指针和不定长数据)的发送与接收。通过结构体定义、内存分配、数据拷贝和序列化方法,详细展示了发送端如何组织数据包,以及接收端如何解析数据包的过程。同时,还给出了更复杂的包结构示例,包括包头和多种类型的内容,进一步阐述了数据包的处理方式。
摘要由CSDN通过智能技术生成

      在学习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<QHostAddress> 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;
            }

        }
    }

}

 

最后效果如下图:

 

 

 

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

 

 

迷你仪表盘是一种自定义控件,主要用于显示简单的数据指示或状态显示。在Qt/C++中编写自定义迷你仪表盘需要以下步骤: 1. 创建一个新的自定义控件类,可以继承QWidget或QLabel。在类的构造函数中初始化一些基本属性,例如仪表盘的大小、颜色、范围等。 2. 重写paintEvent函数,该函数用于绘制仪表盘的外观。在paintEvent函数中,可以使用QPainter绘制各种图形元素,例如圆形、刻度线、指针等。可以根据数据范围和当前值来动态改变指针的位置或角度。 3. 定义一些公有的函数或信号槽,用于设置或更新仪表盘的值。例如,可以定义一个setValue函数用于设置当前值,并在函数内部调用update函数来触发重绘事件。 4. 在主窗口或其他需要使用仪表盘的地方,实例化自定义的迷你仪表盘控件,并设置一些属性,例如位置、大小、初始值等。还可以通过设置样式表来对仪表盘进行外观的自定义。 5. 根据需要,可以添加一些交互功能。例如,可以响应鼠标点击事件,以实现互动操作,例如点击指针可以通过setValue函数来设置对应的值。 编写自定义控件需要一定的Qt/C++编程基础和绘图知识,掌握了基本的绘图函数和事件处理机制后,可以根据需求进行扩展和优化。同时,为了提高代码的可重用性和可维护性,可以将控件的逻辑和外观分离,采用MVC设计模式或其他架构模式进行开发。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值