Qt通过UDP传图片,实现自定义分包和组包

本文介绍了一种基于UDP协议的图像传输方案,详细解释了如何通过分包与组包技术处理大数据量的图像信息。文章首先定义了一个包头结构体,用于标记每包数据的特征,接着描述了分包过程,将图像数据根据UDP_MAX_SIZE大小进行分割,并附带了具体的代码示例。最后,阐述了组包过程,即在接收端如何依据包头信息重组图像,确保数据的完整性和正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.包头结构体

//包头
struct PackageHeader
{
    //包头大小(sizeof(PackageHeader))
    unsigned int uTransPackageHdrSize;
    //当前包头的大小(sizeof(PackageHeader)+当前数据包长度)
    unsigned int uTransPackageSize;
    //数据的总大小
    unsigned int uDataSize;
    //数据被分成包的个数
    unsigned int uDataPackageNum;
    //数据包当前的帧号
    unsigned int uDataPackageCurrIndex;
    //数据包在整个数据中的偏移
    unsigned int uDataPackageOffset;
};


每包数据由包头和包体组成,包头用于标记每包数据的特征,包体是图片根据指定大小分出的每段数据,这里指定大小为const int UDP_MAX_SIZE=1200。
为何要分包可参考:TCP、UDP数据包大小的限制

二.分包与组包

1.分包

 

  int dataLength=buffer.data().size();
 
    unsigned char *dataBuffer=(unsigned char *)buffer.data().data();
 
    int packetNum = 0;
    int lastPaketSize = 0;
    packetNum = dataLength / UDP_MAX_SIZE;
    lastPaketSize = dataLength % UDP_MAX_SIZE;
    int currentPacketIndex = 0;
    if (lastPaketSize != 0)
    {
        packetNum = packetNum + 1;
    }
 
    PackageHeader packageHead;
 
    packageHead.uTransPackageHdrSize=sizeof(packageHead);
    packageHead.uDataSize = dataLength;
    packageHead.uDataPackageNum = packetNum;
 
    unsigned char frameBuffer[1024*1000];
    memset(frameBuffer,0,1024*1000);
    while (currentPacketIndex < packetNum)
    {
        if (currentPacketIndex < (packetNum-1))
        {
            packageHead.uTransPackageSize = sizeof(PackageHeader)+UDP_MAX_SIZE;
            packageHead.uDataPackageCurrIndex = currentPacketIndex+1;
            packageHead.uDataPackageOffset = currentPacketIndex*UDP_MAX_SIZE;
            memcpy(frameBuffer, &packageHead, sizeof(PackageHeader));
            memcpy(frameBuffer+sizeof(PackageHeader), dataBuffer+packageHead.uDataPackageOffset, UDP_MAX_SIZE);
 
            int length=udpsocketSend->writeDatagram(
                        (const char*)frameBuffer, packageHead.uTransPackageSize,
                        QHostAddress(ui->lineEditIP->text()), ui->lineEditPort->text().toInt());
 
            if(length!=packageHead.uTransPackageSize)
            {
              qDebug()<<"Failed to send image";
            }
 
            currentPacketIndex++;
        }
        else
        {
            packageHead.uTransPackageSize = sizeof(PackageHeader)+(dataLength-currentPacketIndex*UDP_MAX_SIZE);
            packageHead.uDataPackageCurrIndex = currentPacketIndex+1;
            packageHead.uDataPackageOffset = currentPacketIndex*UDP_MAX_SIZE;
            memcpy(frameBuffer, &packageHead, sizeof(PackageHeader));
            memcpy(frameBuffer+sizeof(PackageHeader), dataBuffer+packageHead.uDataPackageOffset, dataLength-currentPacketIndex*UDP_MAX_SIZE);
            int length=udpsocketSend->writeDatagram(
                        (const char*)frameBuffer, packageHead.uTransPackageSize,
                        QHostAddress(ui->lineEditIP->text()), ui->lineEditPort->text().toInt());
 
            if(length!=packageHead.uTransPackageSize)
            {
              qDebug()<<"Failed to send image";
            }
 
            currentPacketIndex++;
        }
    }


先将图片转换为QBuffer,这样就可以获取图片的数据和长度,然后根据指定大小UDP_MAX_SIZE分包。在包头中设置每包数据的特征,然后将包体加到包头后面发送出去。
2.组包

       

   static int num = 1;
           static uint size = 0;
 
           PackageHeader *packageHead = (PackageHeader *)datagram.data();
           if (packageHead->uDataPackageCurrIndex == num)
           {
               num++;
               size += packageHead->uTransPackageSize-packageHead->uTransPackageHdrSize;
               if (size > 1024*1000)
               {
                   qDebug()<<"image too big";
                   num = 1;
                   size = 0;
                   return;
               }
               if (packageHead->uDataPackageOffset > 1024*1000)
               {
                   qDebug()<<"image too big";
                   packageHead->uDataPackageOffset = 0;
                   num = 1;
                   size = 0;
                   return;
               }
 
               memcpy(imageData.data+packageHead->uDataPackageOffset, datagram.data()+packageHead->uTransPackageHdrSize,
                      packageHead->uTransPackageSize-packageHead->uTransPackageHdrSize);
               if ((packageHead->uDataPackageNum == packageHead->uDataPackageCurrIndex)
                       && (size == packageHead->uDataSize))
               {
                   imageData.length = packageHead->uDataSize;
 
                   QImage image;
                   image.loadFromData((uchar *)imageData.data,imageData.length,"JPG");
                   QPixmap pixmap=QPixmap::fromImage(image);
                   ui->labelImage_2->setPixmap(pixmap);
                   recvImageNum++;
                   ui->lineEditRevFrame->setText(QString::number(recvImageNum));
                   ui->lineEditRevSize->setText(QString::number(imageData.length));
                   memset(&imageData,0,sizeof(UdpUnpackData));
                   num = 1;
                   size = 0;
               }
           }
           else
           {
               num = 1;
               size = 0;
               memset(&imageData,0,sizeof(UdpUnpackData));
           }


组包是分包的逆过程,根据包头中“数据的总大小”和“数据被分成包的个数”两个字段可以判断组包是否完整,如果完整就在接收端显示出来。
三.示例


界面包括发送端和接收端,接收端的IP地址是自动获取的本机IP地址,上图将发送端的IP地址设置为与接收端IP地址相同,可实现自发自收。

每秒帧数可实时设置每秒发送图片的张数,发送帧数表示一共发送了多少张,接收帧数表示一共接收了多少张,图片大小表示每张图片多少Byte。

可将示例运行于两台计算机,实现双向收发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值