首页 > 代码库 > Qt中QUdpSocket序列化问题
Qt中QUdpSocket序列化问题
写了一个小的Qt网络程序,很简单,发送的网络消息除了字符串还有一个结构体。很简单的想到用memcpy()函数来将数据序列化为BYTE数组从而实现网络传输。
序列化是Java中一个概念,C中并没有,C++中后来引入了序列化和反序列化的概念。序列化是指将非系统类型的类的对象通过序列化操作转换成基本数据格式,从而达到便于网络传输或者文件读写的目的。反序列化是序列化的逆操作。
注:C/C++中BYTE与Char在内存中数据一样,形式相同,在Windef.h中有一行:
typedef unsigned char BYTE;
故而,在内存中char和BYTE是一样的,序列化的目标为BYTE和char是等效的。
而QUdpSocket需要的是char型的网络传输格式,序列化的结果就是BYTE或者char型。
而序列化为char型,就不得不提C/C++中的内存对准问题,个人的经验是尽量多用char、BYTE等定长类型,少用int,double等变长类型,来进行序列化,因为在不同系统中,变长类型会因为系统的不同而定义为不同长度的内存位,进而系统会特定性的进行内存对齐,导致序列化后的反序列化出现乱码。
实在需要使用类似的结构或者类型,则尽量将其岔开,并且通过不同长度类型隔开来保证内存始终对齐至理想位置。并且,相对的,尽量将“较大”的类型变量靠前放置。
-------------------------------------------------------------------------------------------------------
现在描述遇到的问题:
需求:通过Qt界面得到操作信息,并保存到一个结构体中,初始化一个UDPSocket对象,将结构体序列化为char型后,通过网络发送。
实现:Qt界面通过QImage类的对象,得到图像信息并获取操作数据。自定义结构体如下:
1 typedef struct tagDrawInfo 2 { 3 TOOLS m_Tool; /// 绘图工具;TOOLS为自定义enum联合体。 4 int width; /// 画笔宽度; 5 QColor lineColor; /// 线条颜色; 6 QColor fillColor; /// 填充颜色; 7 Qt::BrushStyle 8 brushStyle; /// 画刷风格; 9 QPoint startPoint; /// 开始点;10 QPoint endPoint; /// 结束点;11 }DRAWINFO;
通过memcpy()函数来实现内存拷贝,达到序列化的目的。
问题:接收端得到的DRAWINFO对象,值为意料之外值,进而程序不能通过当前对象完成进一步操作。
解决:1)自己定义序列化函数,将特定对象放置于指定位置,保证反序列化时数据位置和预期一样。分别对结构体的每部分序列化和反序列化,利用各类型的构造函数初始化对象,完成反序列化。
2)对DRAWINFO定义顺序进行调整,对变长类型对象进行分离,确保系统内存的自动对齐不会对数据位置产生影响。更改过后的结构体定义如下:
1 typedef struct tagDrawInfo 2 { 3 Qt::BrushStyle 4 brushStyle; /// 画刷风格; 5 QPoint endPoint; /// 结束点; 6 QColor fillColor; /// 填充颜色; 7 QColor lineColor; /// 线条颜色; 8 TOOLS m_Tool; /// 绘图工具;TOOLS为自定义enum联合体。 9 QPoint startPoint; /// 开始点;10 int width; /// 画笔宽度;11 }DRAWINFO;
结果:对定义顺序进行调整后,接收端的DRAWINFO对象反序列化功能正常。
分析:C++编译器,会对int型、bool型等边长类型进行内存对齐,提高内存利用效率,进而提高CPU一个工作周期内可处理指令条数,提高程序运行效率,但是在自动完成一些功能时会产生意外的问题,需要适当调整。
而关于内存对准的问题,今天欠下一笔,日后有时间进行详解~
Qt中QUdpSocket序列化问题