qt 串口粘包_Qt Socket 收发图片——图像拆包、组包、粘包处理

之前给大家分享了一个使用python发图片数据、Qt server接收图片的Demo。之前的Demo用于传输小字节的图片是可以的,但如果是传输大的图片,使用socket无法一次完成发送该怎么办呢?本次和大家分享一个对大的图片拆包、组包、处理粘包的例子。

程序平台:ubuntu 、 Qt 5.5.1

为了对接收到的图像字节进行组包,我们需要对每包数据规定协议,协议如下图:

每包数据前10个字节对应含义如下:前两个字节对应数据包类型,中间四字节预留,最后四字节是包内数据实际长度。对应协议图片更方便刚开始上手的兄弟理解。

对协议有了一个了解后,接下来说下程序结构。客户端按照协议发送图片字节,服务器接收字节,如果客户端发多少服务器就收多少那可真是太好了,然而意外总是如期而至。服务器这边由于socket的缓冲总是会粘包,所以服务器这边主要工作是拆包和组包,这也是整个程序组中最重要的部分。其次就是服务器在接收图片时为了响应更及时,单独使用一个线程进行接收图片,这里面我使用的是Qt的moveToThread。也使用过linux的socket以及线程接收图片,感觉性能要比Qt封装过的要好,大家有需要的话可以在公众号后台留言。

接下来跟着程序走:

客户端发送部分:

①读取图片字节

void Widget::on_pbn_readPicture_clicked()

{

m_picturePath = m_picturePath +"/auboi5.jpg";

QPixmap pix;

bool ret = pix.load(m_picturePath);

QBuffer buffer;

buffer.open(QIODevice::ReadWrite);

bool ret2 = pix.save(&buffer,"jpg");

m_pictureByteArray = buffer.data();

if(ret2)

{

QString str = "read image finish!";

ui->textEdit->append(str);

}

}

读取图片字节主要用到了Qt的QPixmap 类,这个不细说,大家具体可参考Qt文档。图片字节被读取到m_pictureByteArray中,成功后在textEdit显示read image finish!。

②发送图像拆包

QByteArray dataPackage;

// command 0 ,package total size

QDataStream dataHead(&dataPackage,QIODevice::ReadWrite);

dataHead << quint16();

dataHead << quint32();

dataHead << quint32(m_pictureByteArray.size());

dataPackage.resize();

mp_clsTcpSocket->write(dataPackage);

dataPackage.clear();

QThread::msleep();

这里我拿医一包数据举例说明。第一包数据是将读取到的整张图片的大小发送出去,以判断接收方接收到的数据是否完整。主要涉及到Qt一些数据类型的转换,如将整型字节存入QByteArray 中使用QDataStream 。之后将数据包大小重新设置为40960,方便服务器处理粘包。

③发送utf8 编码的中文

void Widget::on_pbn_sendChinese_clicked()

{

QByteArray dataPackage;

QByteArray chinese = "阶级终极形态假设!";

//command 3

QDataStream dataTail(&dataPackage,QIODevice::ReadWrite);

dataTail << quint16();

dataTail << quint32();

dataTail << quint32(chinese.size());

dataPackage = dataPackage.insert(,chinese.data(),chinese.size());

dataPackage.resize();

mp_clsTcpSocket->write(dataPackage);

}

这部分直接略过了,大家参考下即可。

2.服务器接收部分(重要):

①线程中槽函数接收图片数据拆包

void TcpServerRecvImage::slot_readClientData()

{

QByteArray buffer;

buffer = mp_clsTcpClientConnnect->readAll();

m_bufferSize = buffer.size();

m_total = m_total + buffer.size();

qDebug() << "socket Receive Data size:" << m_bufferSize << m_total;

if(m_bufferSize == )

{

emit signal_sendImagedataPackage(buffer);

qDebug() << "直接发送";

return;

}

if((m_picture.size() + m_bufferSize) == )

{

m_picture.append(buffer);

emit signal_sendImagedataPackage(m_picture);

m_picture.clear();

qDebug() << "拼接后40960";

return;

}

if((m_picture.size() + m_bufferSize) < )

{

m_picture.append(buffer) ;

qDebug() << "直接拼接";

return;

}

if((m_picture.size() + m_bufferSize) > )

{

//case one

if((m_bufferSize > ) && (m_picture.size() == ))

{

while(m_bufferSize/)

{

QByteArray data = buffer.left();

buffer.remove(,);

emit signal_sendImagedataPackage(data);

m_bufferSize = buffer.size();

if((m_bufferSize/ == ) && (m_bufferSize!=))

{

m_picture.append(buffer);

}

QThread::msleep();

}

return;

}

//case two

if((m_bufferSize > ) && (m_picture.size() > ))

{

int frontLength = - m_picture.size();

QByteArray data = buffer.left(frontLength);

buffer.remove(,frontLength);

m_picture.append(data);

if( == m_picture.size())

{

emit signal_sendImagedataPackage(m_picture);

m_picture.clear();

}

m_bufferSize = buffer.size();

while(m_bufferSize/)

{

QByteArray data = buffer.left();

buffer.remove(,);

emit signal_sendImagedataPackage(data);

m_bufferSize = buffer.size();

if((m_bufferSize/ == ) && (m_bufferSize!=))

{

m_picture.append(buffer);

}

QThread::msleep();

}

return;

}

}

}

程序有那么一点长,我先说下他们在做的事情:

1> 如果接收到的字节是40960字节,直接发到主线程处理数据的槽中

2> 如果接收到的字节加上缓存中的字节数目小于40960,直接将数据追加到 m_picture中 【请原谅我40960没有用宏定义】

3> 如果接收到的字节加上缓存中的字节数目等于40960,直接发送

4> 如果接收到的字节加上缓存中的字节数目大于40960,分两种

①接收到的字节是40960的整数倍

if((m_bufferSize/40960 == 0) && (m_bufferSize!=0))

{

m_picture.append(buffer);

}

如果不加上面这个追加函数,则会有数据解析失败

②接收到的字节不是40960的整数倍

int frontLength = 40960 - m_picture.size();

QByteArray data = buffer.left(frontLength);

buffer.remove(0,frontLength);

先取出那一包数据剩余的部分,然后拼成一包发出。

之前试过直接追加到m_picture中,但经常有数据解析失败,

然后看例子,试了这个,结果......

②主线程处理40960数据包

void Widget::slot_imagePackage(QByteArray imageArray)

{

m_imageCount++;

QString number = QString::number(m_imageCount);

ui->textEdit->append(number);

QByteArray cmdId = imageArray.left();

QDataStream commandId(cmdId);

quint16 size;

commandId >> size;

if( == size)

{

QByteArray cmdId = imageArray.mid(,);

QDataStream commandId(cmdId);

quint32 size;

commandId >> size;

qDebug() << "图片的总字节数" << size;

}

if( == size)

{

QByteArray cmdId = imageArray.mid(,);

QDataStream commandId(cmdId);

quint32 size;

commandId >> size;

qDebug() << "图片包尾字节数 " << size;

}

if( == size)

{

QByteArray cmdId = imageArray.mid(,);

QDataStream commandId(cmdId);

commandId >> m_dataSize;

qDebug() << "汉子字节数" << size;

}

switch (size)

{

case :

imageArray.remove(,);

m_imagePackage.append(imageArray);

break;

case :

imageArray.remove(,);

m_imagePackage.append(imageArray);

m_pix.loadFromData(m_imagePackage,"jpg");

ui->lb_image->setPixmap(m_pix.scaled(595.2,)); // 500 * 375

break;

case :

imageArray.remove(,);

imageArray.resize(m_dataSize);

ui->textEdit->append(QTextCodec::codecForMib()->toUnicode(imageArray));

break;

default:

break;

}

}

这部分简单介绍下。识别对应命令ID,对对应的数据包处理。这里面我没有对图像总的接收到的数据判断,大家具体情况具体处理。

(QTextCodec::codecForMib(106)->toUnicode(imageArray) 这个是对QByteArray转换为utf8编码的处理,最后得到的是中文。

最后看下结果图:

服务器接收---->>>

客户端发送--->>>

服务器我在windows下试过,接收数据处理不对,有机会我会再研究下的。

刚开始写这种图片组包的程序没什么经验,写出来是为了让更多刚接触编程的同志不再那么孤立无援!共勉!

需要整个工程的公众号后台留言~

[转]java nio解决半包 粘包问题

java nio解决半包 粘包问题 NIO socket是非阻塞的通讯模式,与IO阻塞式的通讯不同点在于NIO的数据要通过channel放到一个缓存池ByteBuffer中,然后再从这个缓存池中读出数 ...

python socket的应用 以及tcp中的粘包现象

1,socket套接字 一个接口模块,在tcp/udp协议之间的传输接口,将其影藏在socket之后,用户看到的是socket让其看到的. 在tcp中当做server和client的主要模块运用 #s ...

Unity C# 自定义TCP传输协议以及封包拆包、解决粘包问题

本文只是初步实现了一个简单的TCP自定协议,更为复杂的协议可以根据这种方式去扩展. TCP协议,通俗一点的讲,它是一种基于socket传输的由发送方和接收方事先协商好的一种消息包组成结构,主要由消息头 ...

netty10---分包粘包

客户端:根据 长度+数据 方式发送 package com.server; import java.net.Socket; import java.nio.ByteBuffer; public cla ...

网络编程3 网络编程之缓冲区&;subprocess&;粘包&;粘包解决方案

1.sub简单使用 2.粘包现象(1) 3.粘包现象(2) 4.粘包现象解决方案 5.struct学习 6.粘包现象升级版解决方案 7.打印进度条

goim socket丢包粘包问题解决。

-(NSInteger)bytesToInt:(unsigned char*) data { return (data[3]&255)|(data[2]&255)<<8|( ...

mina框架tcpt通讯接收数据断包粘包处理

用mina做基于tcp,udp有通讯有段时间了,一直对编码解码不是很熟悉,这次做项目的时候碰到了断包情况,贴一下解决过程, 我接受数据格式如下图所示: unit32为c++中数据类型,代表4个字节,由 ...

python--socket粘包

socket粘包 1 什么是粘包 须知:只有TCP有粘包现象,UDP永远不会粘包,首先需要掌握一个socket收发消息的原理, 所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少 ...

TCP粘包/拆包问题

无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河 ...

随机推荐

生成解决方案,主项目的bin目录下没有其他项目生成的dll

问题说明: 我的项目组成: 主项目为:TaskUtlity 在生成解决方案的时候在TaskUtlity的bin目录下老是找不到ProBonus项目生成的dll. 解决方案: 1.打开sln文件,找到P ...

CentOS中通过stat查看文件的元数据

CentOS中可以通过stat查看文件的元数据 [baby@xiaoxiao abc]$ stat honey  File: `honey'  Size: 25         Blocks: 8   ...

hadoop 2.6.0上安装sqoop-1.99.6-bin-hadoop200

第一步:下载sqoop-1.99.6-bin-hadoop200.tar.gz  地址:http://www.eu.apache.org/dist/sqoop/1.99.6/ 第二步:将下载好的sqo ...

map,set,list等集合解析以及HashMap,LinkedHashMap,TreeMap等该选谁的的区别

前言: 今天在整理一些资料时,想起了map,set,list等集合,于是就做些笔记,提供给大家学习参考以及自己日后回顾. Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允 ...

GPU版的tensorflow在windows上的安装时的错误解决方案

1.用vs编译cuda的sample时会提示找不到"d3dx9.h"."d3dx10.h"."d3dx11.h"头文件的错误,如果没有安装这 ...

PostgreSQL 的 distinct on 的理解

摘录自:http://www.cnblogs.com/gaojian/archive/2012/09/05/2671381.html 对于 select distinct on , 可以利用下面的例子 ...

SSM实现秒杀系统案例

---------------------------------------------------------------------------------------------[版权申明:本 ...

【lintcode】二分法总结 II

Half and Half 类型题 二分法的精髓在于判断目标值在前半区间还是后半区间,Half and Half类型难点在不能一次判断,可能需要一次以上的判断条件. Maximum Number in ...

关键字(5):cursor游标:(循环操作批量数据)

declare   cursor stus_cur is select * from students;  --定义游标并且赋值(is 不能和cursor分开使用)    cur_stu studen ...

【LeetCode每天一题】Median of Two Sorted Arrays(两数组中的中位数)

There are two sorted arrays nums1 and nums2 of size m and n respectively.  Find the median of the tw ...

你可能感兴趣的:(qt,串口粘包)