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

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

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

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

struct MsgBody
{int iValue;     // 下面两个vector的sizedouble 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 序列化和反序列化不能重载 stringQString 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.mzph.cn/news/862176.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】拷贝构造函数

目录 前言 一、什么是拷贝构造函数&#xff1f; 拷贝构造函数的定义 拷贝构造函数的调用 二、拷贝构造函数的应用 三、拷贝构造函数的最佳实践 四、拷贝构造函数的常见问题 死递归 未使用常量引用 五、总结 前言 在C编程中&#xff0c;拷贝构造是一个重要的概念。理解…

MacOS原版镜像iso下载

苹果公司不提供 macOS 系统的官方 ISO 镜像下载。相反&#xff0c;macOS 系统的更新和安装通常通过 Mac App Store 进行。如果你需要创建一个 macOS 安装盘或 USB 驱动器&#xff0c;你可以直接从 Mac App Store 下载完整的 macOS 安装程序&#xff0c;并使用内置的工具来创建可…

IDEA 插件推荐【一】

好使的插件可以让工作事倍功半。下面就推荐一些常用的IDEA插件&#xff0c;如果你有其他好使的插件&#xff0c;欢迎评论区留言分享出来~ 1.Key Promoter X Key Promoter X 插件&#xff0c;IDEA 快捷键提示工具。 在每次我们使用鼠标进行 IDEA 的某个操作&#xff0c;Key Pr…

lambda-map.merge

map.merge 结论: 1.当前传入的 key ,value biFunction 2.如果之前map不存在则直接put(当前key,当前value) 3.如果之前map已经有了,老value与 当前value 进入function处理后再 put(当前key,处理后的value)

IDEA使用Apidocx插件在RAP生成接口文档

第一步 安装插件&#xff0c;安装最新的1.1.7即可&#xff0c;插件与idea版本对照 第二步 输入对应的IP或域名&#xff0c;端口说明&#xff1a; 1. 38080&#xff1a;为后端数据 API 服务器&#xff08;rap2-delos&#xff09; 2. 3000&#xff1a;为前端静态资源服务&…

安全技术和防火墙(二)

接上一节 备份和还原 iptables-save > /opt/iptables.bak iptables-restore < /opt/iptables.bak snat和dnat snat源地址转换 内网到外网 内网ip转换成可以访问外网的ip 内网的多个主机可以只有一个有效的公网ip地址访问外部网络 dnat 目的地址转发 外部用户&#…

40岁学习java是否需要报班学习?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「java的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;应该不需要。各种公开免费的…

【计算机毕业设计】087基于微信小程序社区养老服务

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

PostgreSQL复制表

PostgreSQL复制表 在 PostgreSQL 中&#xff0c;复制表通常意味着创建表的一个副本&#xff0c;包括其结构、数据、索引、约束等。以下是几种复制表的方法&#xff1a; 仅复制表结构 使用 CREATE TABLE … AS 语句&#xff0c;但不包含任何数据&#xff1a; CREATE TABLE n…

AI加持,商业智能与分析软件市场释放更大潜能

根据IDC最新发布的《中国商业智能和分析软件市场跟踪报告&#xff0c;2023H2》显示&#xff0c;2023下半年&#xff0c;中国商业智能与分析软件市场规模为5.2亿美元&#xff0c;同比增长为3.7%。其中&#xff0c;本地部署收入占比为89.3%&#xff0c;同比增长1.7%&#xff1b;公…

大势智慧有软件可以做激光点与倾斜的融合建模吗?

答&#xff1a;重建大师可以融合建模 重建大师是一款专为超大规模实景三维数据生产而设计的集群并行处理软件&#xff0c;输入倾斜照片&#xff0c;激光点云&#xff0c;POS信息及像控点&#xff0c;输出高精度彩色网格模型&#xff0c;可一键完成空三、自动建模和LOD构建。 …

【MySQL】架构体系概览

本文使用的MySQL版本是8.0 MySQL架构 ​MySQL架构整体由外部程序和MySQL服务器构成。其中内部服务器分成连接层&#xff0c;服务层&#xff0c;服务管理和公共组件&#xff0c;存储引擎层和文件系统层。 连接层 连接层的作用是处理客户端的连接。 网络端口 一台MySQL服务器…

C++精解【8】

文章目录 运算,- 加减法* / 乘除法逐元 乘法逐元 除法逐元综合运算矩阵乘法与加减法 转置、共轭、伴随矩阵点乘法,叉积 运算 ,- 加减法 逐元加减法 #include <iostream> #include "e:/eigen/Eigen/Dense" using namespace std;int main() {Eigen::Matrix2d …

clip系列改进Lseg、 group ViT、ViLD、Glip

Lseg 在clip后面加一个分割head&#xff0c;然后用分割数据集有监督训练。textencoder使用clip&#xff0c;frozen住。 group ViT 与Lseg不同&#xff0c;借鉴了clip做了真正的无监督学习。 具体的通过group block来做的。使用学习的N个group token&#xff08;可以理解为聚类…

计算机毕业设计hadoop+spark+hive知识图谱医生推荐系统 医生数据分析可视化大屏 医生爬虫 医疗可视化 医生大数据 机器学习 大数据毕业设计

测试过程及结果 本次对于医生推荐系统测试通过手动测试的方式共进行了两轮测试。 &#xff08;1&#xff09;第一轮测试中执行了个20个测试用例&#xff0c;通过16个&#xff0c;失败4个&#xff0c;其中属于严重缺陷的1个&#xff0c;属于一般缺陷的3个。 &#xff08;2&am…

Ueditor中集成135编辑器

一、背景 在资讯项目平台运营过程中&#xff0c;资讯需要排版&#xff0c;一般都是在135编辑器排好以后&#xff0c;复制到平台中UEditor编辑器中&#xff0c;所以&#xff0c;他们建议集成一下135哈 二、了解135编辑器 开始调研了解135编辑器&#xff0c;发现人家就支持集成…

Golang | Leetcode Golang题解之第202题快乐数

题目&#xff1a; 题解&#xff1a; func isHappy(n int) bool {cycle : map[int]bool{4: true, 6: true, 37: true, 58: true, 89: true, 145: true, 42: true, 20: true}for n ! 1 && !cycle[n] {n step(n)}return n 1 }func step(n int) int {sum : 0for n > …

AI数据分析007:根据Excel表格数据绘制柱形图

文章目录 一、介绍二、输入内容三、输出内容一、介绍 将Excel文件中2013年至2019年间线上图书的销售额,以条形图的形式呈现,每个条形的高度代表相应年份的销售额,同时在每个条形上方标注具体的销售额数值 二、输入内容 在deepseek中输入提示词: 你是一个Python编程专家,…

SMTP 转发器/中继

设置中继邮件服务器 我将设置一个邮件服务器&#xff0c;该服务器稍后将用作 SMTP 中继服务器。首先&#xff0c;在 Digital Ocean 中创建了一个新的 Ubuntu Droplet&#xff1a; Postfix MTA 安装在droplet上&#xff0c;并带有&#xff1a; apt-get install postfix 在pos…

【Python实战因果推断】4_因果效应异质性4

目录 Cumulative Gain Target Transformation Cumulative Gain 如果采用与累积效应曲线完全相同的逻辑&#xff0c;但将每个点乘以累积样本 Ncum/N&#xff0c;就会得到累积增益曲线。现在&#xff0c;即使曲线的起点具有最高的效果&#xff08;对于一个好的模型来说&#x…