Qt中实现UDP的分包和组包——参考“草上爬”的博客

一、为啥我们要分包组包?直接发不行吗?

最初你可能会存在这样的疑惑?所以我们需要了解下TCP和UDP在Qt中的区别。这里参考了阿拉丁的博客 顺便引用他的图;至于TCP和UDP相应的C/S架构图也可以通过这个兄弟的博客了解,当然还有TCP和UDP在Qt中如何实现。

①UDP和TCP表格对比

在这里插入图片描述

②在Qt中使用UDP和TCP的一些函数

在这里插入图片描述

在这里插入图片描述

③阅读关于Qt中UDP的writeDatagram

通过阅读下图的文档便可以知道:UDP一次性发送的数据量是有限的,所以我们必须要分包!!! 至于为啥UDP需要分包,TCP不需要,我这里大胆猜想:TCP其实也是需要拆分的,只不过它内部已经实现了我们组包的机制了,其实就是更牛逼的UDP,而今天我们的主题就是要用UDP实现:阉割版本的TCP
在这里插入图片描述

二、直接开整

①一些需要参考的知识类:Qt的一些文件设备类

参考某知乎博主南理汉子的回答,说实话我这里也有一些没有整清楚,文件IO这块,只能说我中午不能学(早晚都学);没懂这块也可以直接跳过这块,然后知道QByteArray就行了。

②包头?

包头有啥作用?很明显包头就是用来补充数据包的信息的,举个例子:包头就是诸葛亮留给刘备去江东的三个妙计,刘备带上三个锦囊,去江东遇到困难就打开锦囊获取里头的妙计(这例子有点牵强说实在)。对于一堆数据来说:包头就是诸葛亮的锦囊妙计 当数据到达对端的时候,直接解析包头,获取信息,通过这些信息,把数据依次排序、组装最终重新组合成新的数据,妙哉!!(ps:众所周知:接收端收到的数据包都是乱序的)下面我们来看看包头结构:
在这里插入图片描述

③如何分包?

很显然,把数据包切成一块一块的就行了如下图:
在这里插入图片描述
在这里插入图片描述
在代码中如何体现:通过memcpy将我们想要的数据,按照指定的大小,拷贝到申请的缓存区即可:需要源码可以去草上爬的博客评论区瞅瞅) 完成把数据切块后,又得把数据包头给加上,很明显还是通过memcpy我们申请缓存区后,先把包头加在前面,在加入数据,如图:
在这里插入图片描述
下面是分包的一个大概流程图:
在这里插入图片描述

代码如何体现:

申请数据缓冲区
memcpy(数据缓冲区开始地址,包头数据,包头大小)memcpy(数据缓冲区开始地址+数据偏移地址,切割的数据,数据大小);

代码如下:切忌不可偷懒直接粘贴代码,下面是一部分代码可供学习参考,如果需要完整源码去“草上爬”的博客评论区获取:

 PackageHeader packageHead;//定义一个包头

    packageHead.uTransPackageHdrSize=sizeof(packageHead);//包头大小
    packageHead.uDataSize = dataLength;//数据的总大小
    packageHead.uDataPackageNum = packetNum;//数据被分成包的个数
    packageHead.wOpcode= nOpcode;

    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;//当前下标+1
            packageHead.uDataPackageOffset = (currentPacketIndex)*UDP_MAX_SIZE;//偏移量=下标*数据包最大量

            memcpy(frameBuffer, &packageHead, sizeof(PackageHeader));//拷贝包头到缓存区
            memcpy(frameBuffer+sizeof(PackageHeader), dataBuffer+packageHead.uDataPackageOffset, UDP_MAX_SIZE);//包头后面+数据

            int length=my_udp->writeDatagram(
                        (const char*)frameBuffer, packageHead.uTransPackageSize,
                        my_ip_port.ip, my_ip_port.port);

            if(length!=packageHead.uTransPackageSize)
            {
                qDebug()<<"Failed to send image";
            }
            else
            {
                // ui->msg_record->append("发送成功包:"+currentPacketIndex);
                qDebug()<<"Success to send image";
            }
            currentPacketIndex++;
        }

④如何组包?

话不多说,直接上图,同样需要一块缓存区,然后通过memcpy把接收到的数据给分为:包头+数据 然后放进申请的缓存区进行取出包头,取出数据,拼接等一系列操作就行了。
在这里插入图片描述
下面是组包流程图:
在这里插入图片描述

来点代码意思意思:直接CV是跑步不起来的

	PackageHeader *packageHead = (PackageHeader *)datagram.data();
	if (packageHead->uDataPackageCurrIndex == num)//如果下标为1则
	{
		num++;//下标++
		size += packageHead->uTransPackageSize-packageHead->uTransPackageHdrSize;//大小为:当前包大小-包头大小
	if (size > 1024*1000)
	{
		ui->msg_record->append("image too big");
		num = 1;
		size = 0;
		return;
	}
	if (packageHead->uDataPackageOffset > 1024*1000)
	{
		ui->msg_record->append("image too big");
		packageHead->uDataPackageOffset = 0;
		num = 1;
		size = 0;
		return;
	 }
	memcpy(imageData.data+packageHead->uDataPackageOffset, datagram.data()+packageHead->uTransPackageHdrSize,
	 packageHead->uTransPackageSize-packageHead->uTransPackageHdrSize);

三、总结

关于解决这个问题: 研究别人的论文确实是以一种解决问题的好途径,CSDN上确实是 “在垃圾堆里找宝贝”
关于网络协议这块一点不足: 需要从底层认真研究网络协议,好好把王道书再瞅瞅。
关于后续安排: 马上踏入工作岗位,得加强对C++的学习,希望一切顺利。

  • 7
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愿天堂没有C++

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值