C++ tcp中的可变长度结构体的序列化和反序列化

近日,在项目里,需要对tcp传输的数据进行序列化和反序列化,有很多方法,记录下来
写在前面:使用tcp传输的时候需要注意字节对齐的问题,在以下代码中统一使用单字节对齐

//单字节对齐   写在结构体定义之上
#pragma pack(1)

第一种 QT
如果是用QT写的,结构体拿到值以后,可以使用QDataStream来进行序列化和反序列化,可以参考这个链接

struct MsgBody
{
	int iValue;     // 下面两个vector的size
	double dValue;
	string strValue;
	vector<int> vStrSize;			
	vector<string> vStr;			

	//写数据到类数据成员中
	void write(QByteArray *data)
	{
		QDataStream streamWriter(data, QIODevice::WriteOnly);
		streamWriter.setVersion(QDataStream::Qt_5_14);

		streamWriter << iValue;
		streamWriter << dValue;
        // string 需要转 QString 才能使用 << ,同时如果 string 中有中文,还需要注意当前的编码格式转换
        // 使用 QDataStream 序列化和反序列化不能重载 string
		QString qstr = QString::fromLocal8Bit(strValue.c_str());
		streamWriter << qstr.toLocal8Bit();

		for (int i = 0; i < iValue; i++)
		{
			int iTemp = vStrSize[i];
			streamWriter << iTemp;
		}
		for (int j = 0; j < iValue; j++)
		{
			string strValue = vStr[j];
			QString qstrValue = QString::fromLocal8Bit(strValue.c_str());
			streamWriter << qstrValue.toLocal8Bit();
		}


	}

	//读数据到类数据成员中
	void read(QByteArray &data)
	{
		QDataStream streamReader(&data, QIODevice::ReadOnly);
		streamReader.setVersion(QDataStream::Qt_4_3);

		streamReader >> iValue;
		streamReader >> dValue;

		QByteArray byValue;
		streamReader >> byValue;
		QString qstr = QString::fromLocal8Bit(byValue);
		strValue = string((const char *)qstr.toLocal8Bit());

		for (int i = 0; i < iValue; i++)
		{
			int iTemp;
			streamReader >> iTemp;
			vStrSize.push_back(iTemp);
		}
		for (int j = 0; j < iValue; j++)
		{
			QByteArray qstrValue;
			streamReader >> qstrValue;
			QString str = QString::fromLocal8Bit(qstrValue);
			string strValue = string((const char *)str.toLocal8Bit());
			vStr.push_back(strValue);
		}

	}
};

在代码中write就是把结构体转为二进制,read就是把二进制转为结构体,使用方法

MsgBody body;
// 对 body 的各个变量赋值
QByteArray packet;
body.write(&packet);

body.read(packet);  // packet为二进制

这种方法就是在使用QT写tcp的时候好用
注:
QT如果是string类型中包含有中文的话,需要转编码格式,上述的编码格式转换适用于windows

第二种 强转
强转的方法适用于结构体中没有可变长度的变量,可变长度就是结构体中包含string, vector类型

struct NET_HEAD
{
public:
	int recvId;   // 接收消息方	
	int sendId;  // 发送消息方	
	int msgType;     // 消息类型
	int dataLen;     // 数据长度  
};

int main()
{
	char *m_Msg_buf = new char[40960];;//消息缓冲区
	char *m_Recv_buf= new char[4096];;//消息缓冲区
	
	NET_HEAD head;
	
	// 结构体转为二进制
	memcpy((void *)m_Msg_buf, (void *)&head, sizeof(NET_HEAD ));
	
	// s_server 是tcp连接
	int recv_len = recv(s_server, m_Recv_buf, 4096, 0);
	memcpy(m_Msg_buf, m_Recv_buf, recv_len);
	// 二进制强转为结构体	
	NET_HEAD* header = (NET_HEAD*)m_Msg_buf;
}

如果使用QT的话更简单了

// 还是上面的结构体,在main函数中写以下代码
NET_HEAD head;
// 为 head 赋值
QByteArray packet;
packet.append((char*)&head, sizeof(NET_HEAD));  // 将 结构体强转为二进制

// 二进制转为结构体
// packet 是接受到的二进制
NET_HEAD * head1 = (NET_HEAD *)packet.data();  

第三种 纯c++
用纯C++来进行序列化和反序列化可变结构体比较有意思,其实也就是memcpy,参考这个

struct ComplexData {
	int id;
	int len;
	string name;
	int iStrVecLen;
	vector<int> viStr;
	vector<string> vStr;
	int valuesLen;
	vector<double> values;
	char* serialize() const {
		int offset = 0;
		char* buffer = new char[sizeof(ComplexData)];
		memcpy(buffer + offset, &this->id, sizeof(int));
		offset += sizeof(int);

		memcpy(buffer + offset, &this->len, sizeof(int));
		offset += sizeof(int);

		memcpy(buffer + offset, (this->name).data(), this->len);
		offset += this->len;
		offset += 1;   // 加1 是为了防止string类型后面有 \0 

		memcpy(buffer + offset, &this->iStrVecLen, sizeof(int));
		offset += sizeof(int);

		for (int i = 0; i < this->iStrVecLen; i++)
		{
			int iValue = this->viStr[i];
			memcpy(buffer + offset, &iValue, sizeof(int));
			offset += sizeof(int);
		}

		for (int i = 0; i < this->iStrVecLen; i++)
		{
			string str = this->vStr[i];
			int sizeStr = this->viStr[i];
			memcpy(buffer + offset, str.data(), sizeStr);
			offset += sizeStr;
			offset += 1;
		}

		memcpy(buffer + offset, &this->valuesLen, sizeof(int));
		offset += sizeof(int);

		for (int i = 0; i < this->valuesLen; i++)
		{
			memcpy(buffer + offset, &(this->values[i]), sizeof(double));
			offset += sizeof(double);
		}

		return buffer;
	}
	// 反序列化函数
	void deserialize(char* buffer) {
		int offset = 0;
		memcpy(&this->id, buffer + offset, sizeof(int));
		offset += sizeof(int);

		memcpy(&this->len, buffer + offset, sizeof(int));
		offset += sizeof(int);

		this->name = std::string(buffer + offset, this->len);
		offset += this->len;
		offset += 1;

		memcpy(&this->iStrVecLen, buffer + offset, sizeof(int));
		offset += sizeof(int);

		for (int i = 0; i < this->iStrVecLen; i++)
		{
			int iValue;
			memcpy(&iValue, buffer + offset, sizeof(int));
			offset += sizeof(int);
			this->viStr.push_back(iValue);
		}

		for (int i = 0; i < this->iStrVecLen; i++)
		{
			int iValue = this->viStr[i];
			string str = std::string(buffer + offset, iValue);
			offset += iValue;
			offset += 1;
			this->vStr.push_back(str);
		}


		memcpy(&this->valuesLen, buffer + offset, sizeof(int));
		offset += sizeof(int);

		for (int i = 0; i < this->valuesLen; i++)
		{
			double dValue;
			memcpy(&dValue, buffer + offset, sizeof(double));
			offset += sizeof(double);
			this->values.push_back(dValue);
		}

	}
};

int main()
{
	string name = "Join Doe";
	vector<double> vec;
	vec.push_back(1.1);
	vec.push_back(2.2);
	vec.push_back(3.3);

	string str1 = "teards sfdf";
	string str2 = "sdsHJJMHj";
	string str3 = "测试代码";
	string str4 = "123qweer长度";

	vector<string> vecStr;
	vecStr.push_back(str1);
	vecStr.push_back(str2);
	vecStr.push_back(str3);
	vecStr.push_back(str4);

	vector<int> vecIStr;
	vecIStr.push_back(str1.size());
	vecIStr.push_back(str2.size());
	vecIStr.push_back(str3.size());
	vecIStr.push_back(str4.size());

	ComplexData oriData;
	oriData.id = 1;
	oriData.len = name.size();
	oriData.name = name;
	oriData.iStrVecLen = vecStr.size();
	oriData.viStr = vecIStr;
	oriData.vStr = vecStr;
	oriData.valuesLen = vec.size();
	oriData.values = vec;
	
	// 序列化并发送
	char* buffer = oriData.serialize();
	
	// 接收并反序列化
	ComplexData receivedObj;
	receivedObj.deserialize(buffer);

	cout << "recvData.id: " << receivedObj.id << endl;
	cout << " recvData.name: " << receivedObj.name << endl;
	for (int i = 0; i < receivedObj.values.size(); i++)
	{
		cout << receivedObj.values[i] << endl;
	}

	for (int i = 0; i < receivedObj.vStr.size(); i++)
	{
		cout << receivedObj.vStr[i] << endl;
	}

	return 0;
}

纯C++序列化和反序列化对于string类型中有中文的也不用担心编码格式了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/753448.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

php composer 报错

引用文章&#xff1a; Composer设置国内镜像_composer 国内源-CSDN博客 php composer.phar require --prefer-dist yiidoc/yii2-redactor "*" A connection timeout was encountered. If you intend to run Composer without connecting to the internet, run the …

汉江师范学院2024年成人高等继续教育招生简章

汉江师范学院&#xff0c;这所承载着深厚文化底蕴和学术积淀的高等学府&#xff0c;即将在2024年迎来新一季的成人高等继续教育招生。这不仅是一次知识的盛宴&#xff0c;更是对每一位怀揣梦想、追求进步的成年人的诚挚邀请。 汉江师范学院&#xff0c;以其严谨的教学态度、卓…

老师如何发布学校分班情况?

随着新学期的临近&#xff0c;许多老师可能都会回想起过去那些忙碌的日子&#xff0c;他们不得不面对一堆学生名单&#xff0c;手动进行班级分配&#xff0c;然后逐一通知家长和学生&#xff0c;这种工作不仅繁琐而且容易出错&#xff0c;让人倍感压力。 然而&#xff0c;今天我…

真正的IDEA在线版有多好用

前言 在上一篇文章使用过TitanIDE的VS Code在线版以后&#xff0c;尝到了不少甜头&#xff0c;紧接着又去使用了他的在线版IntelliJ IDEA&#xff0c;同样非常惊艳&#xff0c;不需要任何时间去适应这款云原生开发工具,事不宜迟&#xff0c;马上开整 这才是真正的VS Code在线版…

9种慢慢被淘汰的编程语言...【送源码】

技术不断进步&#xff0c;我们使用的编程语言也不例外。 随着人工智能的兴起以及对编程语言使用的影响&#xff0c;我们更加关注哪些语言将在未来继续流行&#xff0c;哪些会被淘汰。 Python、Java 和 JavaScript 等多功能编程语言正在主导市场&#xff0c;而其他一些语言则逐…

第 1 章SwiftUI 简介

在 2019 年的 WWDC 上,Apple 宣布推出一款名为 SwiftUI 的全新框架,令开发者们大吃一惊。该框架不仅改变了开发 iOS 应用的方式,还代表了自 Swift 首次亮相以来 Apple 开发者生态系统最重大的转变。SwiftUI 适用于所有 Apple 平台,包括 iPadOS、macOS、tvOS 和 watchOS,这…

REST API 中的 HTTP 请求参数

当我们在谈论现代 Web 开发时&#xff0c;REST API (Representational State Transfer Application Programming Interface) 扮演着至关重要的角色。它允许不同的系统以一种简洁且高效的方式进行通信。HTTP 请求参数是控制此通信流程中数据如何被发送和接收的重要组成部分。 H…

加密教程:pdf怎么加密?7个pdf加密技巧任你选(图文详解)

pdf作为一种便携式文档&#xff0c;是展示内容的首选格式&#xff0c;目前也已广泛应用于交换和分享重要等温&#xff0c;例如内部报告、人力资源文件&#xff0c;以及商业提案等包含敏感信息的文档。然而&#xff0c;在如今的数字化时代&#xff0c;随着越来越多的企业将其文档…

mfc140.dll怎么安装?mfc140.dll丢失安装详细解决方法

当电脑出现找不到mfc140.dll丢失问题&#xff0c;我们需要怎么办&#xff1f;怎么解决mfc140.dll丢失问题&#xff1f;mfc140.dll到底是什么&#xff1f;下面我给大家详细介绍与分析&#xff0c;最重要的是mfc140.dll的解决方法&#xff01; 一、文件丢失原因分析 在分析mfc14…

golang 获取系统的主机 CPU 内存 磁盘等信息

golang 获取系统的主机 CPU 内存 磁盘等信息 要求 需要go1.18或更高版本 官方地址&#xff1a;https://github.com/shirou/gopsutil 使用 #下载包 go get github.com/shirou/gopsutil/v3/cpu go get github.com/shirou/gopsutil/v3/disk go get github.com/shirou/gopsuti…

PIP安装Python扩展包超时解决办法-国内镜像

问题描述 使用pip安装Python扩展包经常超时&#xff0c;无法安装 解决方法 使用清华大学镜像&#xff1a; https://pypi.tuna.tsinghua.edu.cn/simple/ 使用方法&#xff1a;以openpyxl为例 原来&#xff1a;pip install openpyxl 现在&#xff1a;pip install -i https…

Git与GitLab的企业实战--尚硅谷git课程

Git与GitLab的企业实战 第1章 Git概述 Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。 Git易于学习&#xff0c;占地面积小&#xff0c;性能极快。 它具有廉价的本地库&#xff0c;方便的暂存区域和多个工作流分支等特性…

IEEE JSTSP综述:从信号处理领域分析视触觉传感器的研究

触觉传感器是机器人系统的重要组成部分&#xff0c;虽然与视觉相比触觉具有较小的感知面积&#xff0c;但却可以提供机器人与物体交互过程中更加真实的物理信息。 视觉触觉传感是一种分辨率高、成本低的触觉感知技术&#xff0c;被广泛应用于分类、抓取、操作等领域中。近期&a…

什么是指令微调(LLM)

经过大规模数据预训练后的语言模型已经具备较强的模型能力&#xff0c;能够编码丰富的世界知识&#xff0c;但是由于预训练任务形式所限&#xff0c;这些模型更擅长于文本补全&#xff0c;并不适合直接解决具体的任务。 指令微调是相对“预训练”来讲的&#xff0c;预训练的时…

UG_NX11.0之Windows11中安装出错及解决方法

UG_NX11.0之Windows11中安装出错及解决方法 文章目录 UG_NX11.0之Windows11中安装出错及解决方法1. 安装出错2. 解决方法1. 设置以兼容性模式运行2. 配置环境变量 3. 再次安装问题解决4. 安装后可删除配置的环境变量(可选) 1. 安装出错 以管理员身份运行Launch.exe,如下 点击D…

浅谈逻辑控制器之while控制器

浅谈逻辑控制器之while控制器 “While控制器”是一种高级控制结构&#xff0c;它允许用户基于特定条件来循环执行其下的子采样器或控制器&#xff0c;直至该条件不再满足。本文旨在详细介绍While控制器的功能、配置方法、使用场景以及实践示例&#xff0c;帮助测试工程师高效利…

浅谈红队攻防之道-DLL注入上线cs

等我熬过这一段狼狈&#xff0c;一个人尝尽孤独的滋味&#xff0c;我会笑着与这个世界和解 0x1 DLL注入概念 DLL注入(DLL Injection)是一种计算机编程技术&#xff0c;它可以强行使另一个进程加载一个动态链接库(DLL)以在其地址空间内运行指定代码。常见用途是改变原先程序的…

C++Primer Plus 第十四章代码重用:14.4.4 数组模板示例和非类型参数2

14.4.4 数组模板示例和非类型参数 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右…

SpringBoot整合Solr进行搜索(简单)

SpringBoot整合Solr进行搜索 创建SpringBoot项目pom中加入Solr依赖配置 Solr创建实体编写一个简单的ID查询打印结果 参考文章 创建SpringBoot项目 这里基于aliyun提供的快速构建一个项目。我们这主要是整合Solr。 pom中加入Solr依赖 maven下载地址 pom中加入以下内容&#x…

线程版服务器实现(pthread_server)

用到的所有方法所需要的参数可以在wrap.c文件中查询&#xff0c;wrap中找不到的直接通过man手册查询 1.首先介绍一下我自己写的包裹文件&#xff0c;里面有各种在可能要用到的方法 wrap.c: #include <stdlib.h> #include <stdio.h> #include <unistd.h> #…