Linux--序列化与反序列化

序列化

序列化是指将数据结构或对象状态转换成可以存储或传输的格式的过程。在序列化过程中,对象的状态信息被转换为可以保持或传输的格式(如二进制、XML、JSON等)。序列化后的数据可以被写入到文件、数据库、内存缓冲区中,或者通过网络发送到另一个系统。

序列化的主要目的包括:

  • 持久化:将对象状态保存到存储系统中,以便程序在重新启动时能够重新加载这些数据。
  • 网络传输:在分布式系统中,对象需要通过网络发送到其他节点,序列化是实现这一点的关键步骤。
  • 数据交换:不同的应用程序或系统之间可能需要交换数据,序列化提供了一种标准化的数据格式。

反序列化

反序列化是序列化的逆过程,即将序列化后的数据(如二进制、XML、JSON等)恢复成原始的数据结构或对象状态。在反序列化过程中,原始的数据格式被解析并重新构建成原始对象或数据结构。

反序列化的主要目的是:

  • 恢复对象状态:从存储系统或网络接收的数据中恢复对象的状态。
  • 数据使用:在应用程序中使用反序列化后的数据。

JSON

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它基于JavaScript的一个子集,但是JSON是独立于语言的,许多编程语言都有解析JSON的库,因此JSON成为了现代互联网中数据交换的标准格式之一。

Jsoncpp

Jsoncpp是一个开源的C++库,主要用于解析和生成JSON数据格式。

安装方式:
在这里插入图片描述

特点:

  • 轻量级:Jsoncpp具有很小的代码体积和低的内存占用,非常适合嵌入式系统或资源受限的环境。
  • 跨平台:Jsoncpp可以在多种操作系统上运行,包括Windows、Linux、Mac等。
  • 简单易用:Jsoncpp提供了简洁的API,使得解析和生成JSON数据变得非常容易。开发人员可以通过简单的函数调用来实现JSON数据的读取、修改和生成。
  • 高性能:Jsoncpp采用了高效的算法和数据结构,提供了快速的JSON解析和生成功能。
  • 可靠稳定:Jsoncpp经过广泛的测试和使用,已经成为一个成熟的库,具有良好的稳定性和可靠性。

核心功能:

Jsoncpp的核心数据结构是Json::Value类,它表示JSON数据的各种类型,包括对象、数组、字符串、数字等。通过Json::Value类及其相关方法,开发者可以直观地操作JSON数据,无需关注复杂的内部实现细节。

Jsoncpp还提供了几个重要的类来支持JSON数据的解析和生成,包括:

Json::Reader:用于将JSON字符串或文件解析成Json::Value对象。
Json::Writer:(及其子类如Json::FastWriter、Json::StyledWriter等):用于将Json::Value对象序列化为JSON字符串。

在这里插入图片描述

示例:网络计算器

实现一个服务器版的计算器,客户端将要要计算的两个数发送到服务端,服务端计算好将结果返回给客户端;

期间,我们会用一种结构体存储对应的计算的两个数以及符号;数据通过序列化发送到服务端,服务端需要将字符串反序列化解析得到数据;

Socket.hpp

#include <iostream>
#include <string>
#include <functional>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
#include <sys/types.h>
#include <memory>
#include "InetAddr.hpp"
#include "Log.hpp"namespace socket_ns
{class Socket;const static int gbacklog=8;//默认最大连接数using socket_sptr=std::shared_ptr<Socket>;//套接字指针enum{SOCKET_ERROR = 1,BIND_ERROR,LISTEN_ERROR,USAGE_ERROR};//在基类创建一系列虚函数,只要派生类能用到就在这里创建class Socket{public:virtual void CreateSocketOrDie() =0; //创建套接字virtual void BindSocketOrDie(InetAddr& addr) =0;  //绑定套接字virtual void ListenSocketOrDie()=0; //监听套接字virtual socket_sptr Accepter(InetAddr* addr) =0; //接受客户端virtual bool Connector(InetAddr &addr) = 0; //连接客户端virtual int SockFd() = 0; //获取Sockfdvirtual int Recv(std::string *out) = 0; //接收对方信息virtual int Send(const std::string &in) = 0; //发送给对方信息public://创建监听套接字,将一系列操作细分化,直接引用对应函数直接创建void BuildListenSocket(InetAddr& addr){CreateSocketOrDie();BindSocketOrDie(addr);ListenSocketOrDie();}bool BuildClientSocket(InetAddr &addr){CreateSocketOrDie();return Connector(addr);}};class TcpSocket : public Socket{public:TcpSocket(int sockfd=-1):_sockfd(sockfd){}void CreateSocketOrDie() override  //override明确的重写基类函数{_sockfd=socket(AF_INET,SOCK_STREAM,0);if(_sockfd<0){LOG(FATAL, "socket error");exit(SOCKET_ERROR);}LOG(DEBUG, "socket create success, sockfd is : %d\n", _sockfd);}void BindSocketOrDie(InetAddr& addr) override{struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(addr.Port());local.sin_addr.s_addr = inet_addr(addr.Ip().c_str());int n=bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if (n < 0){LOG(FATAL, "bind error");exit(BIND_ERROR);}LOG(DEBUG, "bind success, sockfd is : %d\n", _sockfd);}void ListenSocketOrDie() override{int n=listen(_sockfd,gbacklog);if (n < 0){LOG(FATAL, "listen error");exit(LISTEN_ERROR);}LOG(DEBUG, "listen success, sockfd is : %d\n", _sockfd);}socket_sptr Accepter(InetAddr* addr) override{struct sockaddr_in peer;socklen_t len=sizeof(peer);int sockfd = accept(_sockfd,(struct sockaddr*)&peer,&len);if (sockfd < 0){LOG(WARNING, "accept error\n");return nullptr;}*addr=peer;socket_sptr sock=std::make_shared<TcpSocket>(sockfd);return sock;}virtual bool Connector(InetAddr& addr){struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family=AF_INET;server.sin_addr.s_addr=inet_addr(addr.Ip().c_str());server.sin_port=htons(addr.Port());int n=connect(_sockfd,(struct sockaddr*)&server,sizeof(server));if (n < 0){std::cerr << "connect error" << std::endl;return false;}return true;}int Recv(std::string *out) override{char inbuffer[1024];ssize_t n = recv(_sockfd,inbuffer,sizeof(inbuffer)-1,0);if (n > 0){inbuffer[n] = 0;*out += inbuffer; // 接收次数可能不只一次,一般是多次的,}return n;}int Send(const std::string &in) override{int n = send(_sockfd,in.c_str(),in.size(),0);return n;}int SockFd() override{return _sockfd;}~TcpSocket(){}private:int _sockfd;};
}

在这里插入图片描述

ProToCol.hpp

主要是发出请求时将数据序列化和接收数据时反序列化;服务端响应时接收数据的反序列化和发送结果时序列化;

#pragma once 
#include <iostream>
#include <string>
#include<unistd.h>
#include<memory>
#include<jsoncpp/json/json.h>namespace protocol_ns
{// 协议的样子:// 报文 = 报头+有效载荷// "有效载荷的长度"\r\n"有效载荷"\r\nconst std::string SEP= "\r\n";// 解决TCP的粘报问题,TCP 读取不全的问题std::string Encode(const std::string &json_str){int json_str_len = json_str.size(); //有效载荷的长度std::string proto_str = std::to_string(json_str_len); //转为stringproto_str += SEP; //+ 分隔符proto_str += json_str;// + 数据字符串proto_str += SEP;// + 分隔符return proto_str; //返回一个报文}//将报文分析出数据字符串出来std::string Decode(std::string &inbuffer){auto pos = inbuffer.find(SEP); //找到分隔符的位置if (pos == std::string::npos)return std::string();std::string len_str = inbuffer.substr(0, pos);//前头的有效数据长度的字符串if (len_str.empty())return std::string();int packlen = std::stoi(len_str);//记录数据字符串的实际长度(传递时的差错主要出在这里)int total = packlen + len_str.size() + 2 * SEP.size(); //报文总长度if (inbuffer.size() < total)return std::string();std::string package = inbuffer.substr(pos + SEP.size(), packlen); //取出数据字符串inbuffer.erase(0, total); //删除掉原先的报文return package;}//请求将我们的数据序列化和反序列化(客户端)    class Request{public:Request(){}Request(int x, int y, char oper) : _x(x), _y(y), _oper(oper){}   //序列化:将结构体数据转换为字符串bool Serialize(std::string* out){Json::Value root; //Json::Value: Json格式的值root["x"] = _x;root["y"] = _y;root["oper"] = _oper;Json::FastWriter writer;*out=writer.write(root); //将Json值转换为字符串return true;}//反序列化:将字符串转换为结构体数据bool DeSerialize(const std::string& in){Json::Value root;Json::Reader reader;//解析字符串bool res=reader.parse(in,root);//将字符串转为Json值,存放于root中if (!res)return false;//再将Json值转为结构体数据_x = root["x"].asInt();_y = root["y"].asInt();_oper = root["oper"].asInt();return true;}public:int _x;int _y;char _oper; //操作符 _x 加减乘除 _y};//将结果序列化和反序列化(服务端)class Response{public:Response(){}Response(int result, int code) : _result(result), _code(code){}bool Serialize(std::string *out){// 转换成为字符串Json::Value root;root["result"] = _result;root["code"] = _code;Json::FastWriter writer;// Json::StyledWriter writer;*out = writer.write(root);return true;}bool Deserialize(const std::string &in){Json::Value root;Json::Reader reader;bool res = reader.parse(in, root);if (!res)return false;_result = root["result"].asInt();_code = root["code"].asInt();return true;}public:int _result; // 结果int _code;   // 0:success 1: 除0 2: 非法操作 3. 4. 5};//创建需求class Factory{public:Factory(){srand(time(nullptr) ^ getpid());opers = "+-*/%^&|";                                                                                                                 }std::shared_ptr<Request> BuildRequest(){int x = rand() % 10 + 1;usleep(x * 10);int y = rand() % 5; // [0,1,2,3,4]usleep(y * x * 5);char oper = opers[rand() % opers.size()];std::shared_ptr<Request> req= std::make_shared<Request>(x,y,oper);return req;}std::shared_ptr<Response> BuildResponse(){return std::make_shared<Response>();}~Factory(){}private:std::string opers;};
}

在这里插入图片描述

CalCulate.hpp

将已经反序列化的数据通过计算得出结果:

#pragma once
#include <iostream>
#include "ProToCol.hpp"using namespace protocol_ns;class Calculate
{
public:Calculate(){}//根据输入的请求通过实际计算转换为结果Response Excute(const Request &req){Response resp(0, 0);switch (req._oper){case '+':resp._result = req._x + req._y;break;case '-':resp._result = req._x - req._y;break;case '*':resp._result = req._x * req._y;break;case '/':{if (req._y == 0){resp._code = 1;}else{resp._result = req._x / req._y;}}break;case '%':{if (req._y == 0){resp._code = 2;}else{resp._result = req._x % req._y;}}break;default:resp._code = 3;break;}return resp;}~Calculate(){}private:
};

在这里插入图片描述

TcpServerMain.cc

#include <iostream>
#include <functional>
#include <memory>
#include "TcpServer.hpp"
#include "ProToCol.hpp"
#include"CalCulate.hpp"
using namespace protocol_ns;using callback_t = std::function<Response(const Request&)>;//响应计算结果的函数指针
void Usage(std::string proc)
{std::cout << "Usage:\n\t" << proc << " local_port\n"<< std::endl;
}//由于接收的是报文,需要通过反序列化和序列化反复转换并且期间要完成计算的服务
class Service
{
public:Service(callback_t cb) : _cb(cb){}//套接字指针:客户端的sockfd   网络地址:客户端void ServiceHelper(socket_sptr sockptr,InetAddr client){int sockfd = sockptr->SockFd();LOG(DEBUG, "get a new link, info %s:%d, fd : %d\n",client.Ip().c_str(),client.Port(),sockfd );std::string clientaddr = "[" + client.Ip() + ":" + std::to_string(client.Port()) + "] ";std::string inbuffer;while(true){sleep(5);Request req;// 1.接收来自客户端的发送数据,接收时是已被序列化的数据信息int n = sockptr->Recv(&inbuffer); if (n < 0){LOG(DEBUG, "client %s quit\n", clientaddr.c_str());break;}std::string package;while (true)//传递途中可能会出现数据丢失,要通过循环直到找到有效正确的数据信息{//2.分析报文中的有效数据sleep(1);std::cout << "inbuffer: " << inbuffer << std::endl;package = Decode(inbuffer);//解析报文if (package.empty())break;std::cout << "------------------------begin---------------" << std::endl;std::cout << "resq string:\n"<< package << std::endl;  // 3.反序列化,将字符串变为结构体信息req.DeSerialize(package);// 4. 业务处理Response resp = _cb(req);// 5. 对应答做序列化std::string send_str;resp.Serialize(&send_str);std::cout << "resp Serialize:" << std::endl;std::cout << send_str << std::endl; // 6. 添加长度报头send_str = Encode(send_str);std::cout << "resp Encode:" << std::endl;std::cout << send_str << std::endl;// "len"\r\n"{             }"\r\n"sockptr->Send(send_str); // 本次不对发送做处理, EPOLL     }}}
private:callback_t _cb; //将需求转换为结果
};// ./tcpserver port
int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);return 1;}uint16_t port = std::stoi(argv[1]);Calculate cal;Service calservice(std::bind(&Calculate::Excute, &cal, std::placeholders::_1));//通过Service解决报文问题io_service_t service = std::bind(&Service::ServiceHelper, &calservice, std::placeholders::_1, std::placeholders::_2);//将服务和服务器耦合std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(port, service);tsvr->Loop();return 0;
}

在这里插入图片描述

TcpClientMain.cc

#include <iostream>
#include <string>
#include <memory>
#include <ctime>
#include "Socket.hpp"
#include "ProToCol.hpp"
#include "InetAddr.hpp"using namespace socket_ns;
using namespace protocol_ns;void Usage(std::string proc)
{std::cout << "Usage:\n\t" << proc << " serverip serverport\n"<< std::endl;           
}int main(int argc,char* argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}std::string serverip = argv[1];//服务端ipuint16_t serverport = std::stoi(argv[2]);//服务端端口号InetAddr serveraddr(serverip, serverport);//创建网络地址Factory factory;//std::shared_ptr<Socket> cli=std::make_shared<TcpSocket>();//创建套接字的智能指针bool res=cli->BuildClientSocket(serveraddr); //创建Client套接字并连接到服务端std::string inbuffer; //接收应答时存储对方的数据while (res){sleep(1);std::string str;//一次创建5个请求出来//for(int i=0;i<5;i++)//{// 1. 构建一个请求auto req = factory.BuildRequest();// 2. 对请求进行序列化std::string send_str; //序列化后保存于此req->Serialize(&send_str);std::cout << "Serialize: \n"<< send_str << std::endl;// 3. 添加长度报头send_str = Encode(send_str);std::cout << "Encode: \n"<< send_str << std::endl;str += send_str;//}//4.发送报文cli->Send(str);// 5. 读取应答int n = cli->Recv(&inbuffer);//接收应答if (n <= 0)break;std::string package = Decode(inbuffer); //解析报文if (package.empty()) //一旦为空那么继续解析下一个报文continue;// 6. 反序列化auto resp = factory.BuildResponse();resp->Deserialize(package);std::cout<<"result: "<<resp->_result<<"["<< resp->_code <<"]"<<std::endl;}
}

结果

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

当年很流行,现在已经淘汰的Java技术,请不要学了!【建议收藏】

在Java技术的发展历程中&#xff0c;确实有一些曾经流行但现在已经被淘汰或不再推荐使用的技术。了解这些技术可以帮助你避免学习过时的知识&#xff0c;从而更高效地提升自己的技能。 以下是一些曾经流行但现在已经不太推荐学习的Java技术&#xff1a; 1. Servlet 2.x&#x…

谷粒商城实战笔记-71-商品服务-API-属性分组-前端组件抽取父子组件交互

文章目录 一&#xff0c;一次性创建所有的菜单二&#xff0c;开发属性分组界面1&#xff0c;左侧三级分类树形组件2&#xff0c;右侧分组列表3&#xff0c;左右两部分通信3.1 子组件发送数据3.2&#xff0c;父组件接收数据 Vue的父子组件通信父组件向子组件传递数据子组件向父组…

【odoo17】后端py方法触发右上角提示组件

概要 在前面文章中&#xff0c;有介绍过前端触发的通知服务。 【odoo】右上角的提示&#xff08;通知服务&#xff09; 此文章则介绍后端触发方法。 内容 直接上代码&#xff1a;但是前提一定是按钮触发&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; def bu…

自动化测试 pytest 中 scope 限制 fixture使用范围!

导读 fixture 是 pytest 中一个非常重要的模块&#xff0c;可以让代码更加简洁。 fixture 的 autouse 为 True 可以自动化加载 fixture。 如果不想每条用例执行前都运行初始化方法(可能多个fixture)怎么办&#xff1f;可不可以只运行一次初始化方法&#xff1f; 答&#xf…

17.延迟队列

介绍 延迟队列&#xff0c;队列内部是有序的&#xff0c;延迟队列中的元素是希望在指定时间到了以后或之前取出和处理。 死信队列中&#xff0c;消息TTL过期的情况其实就是延迟队列。 使用场景 1.订单在十分钟内未支付则自动取消。 2.新创建的店铺&#xff0c;如果十天内没…

【Ant Design Vue的更新日志】

🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步! 以下是Ant Design Vue的更新日志 版本1.7.0(发布日期:2023年4月) …

TCP/IP协议——使用Socket套接字实现

目录 Socket 使用Socket实现TCP客户端和服务器的过程 使用Socket搭建TCP服务器 线程优化 向客户端发送消息 连接的断开 客户端主动断开 服务端主动断开 服务器完整的程序 使用Socket编写客户端程序连接TCP服务器 Socket Socket是一种网络通信协议&#xff0c;它允许…

渗透测试:筑牢网络安全的坚固防线

在当今这个互联网高度发达的时代&#xff0c;网络安全已成为维护社会稳定和经济发展的重要基石。随着互联网的普及&#xff0c;网络攻击手段日益复杂多变&#xff0c;各类安全威胁层出不穷。为了有效应对这些挑战&#xff0c;渗透测试作为一种重要的安全测试与评估方法&#xf…

arduino程序-数字输出-学用led(led电路及相关函数)(基础知识)

arduino程序-数字输出-学用led&#xff08;led电路及相关函数&#xff09;&#xff08;基础知识&#xff09; 1-10 数字输出1-学用ledLED发光二极管LED电压特性电阻 1-11 数字输出arduino控制LEDLed与arduino连接电路图高电平及低电平含义 1-10 数字输出1-学用led 元器件初步介…

关于 AGGLIGATOR(猛禽)网络宽频聚合器

AGGLIGATOR 是一个用于多个链路UDP/IP带宽聚合的工具软件&#xff0c;类似MTCP的作用&#xff0c;不过它是针对UDP/IP宽频聚合的。 举个例子&#xff1a; 中国大陆有三台公网服务器&#xff0c;中国香港有一台大带宽服务器。 那么&#xff1a; AGGLIGATOR 允许中国大陆的客户…

Day7-指针专题二

1. 字符指针与字符串 C语言通过使用字符数组来处理字符串 通常&#xff0c;我们把char数据类型的指针变量称为字符指针变量。字符指针变量与字符数组有着密切关系&#xff0c;它也被用来处理字符串 初始化字符指针是把内存中字符串的首地址赋予指针&#xff0c;并不是把该字符串…

独占电脑资源来执行一个应用

1. 背景 在人工智能时代&#xff0c;随着神经网络的发展&#xff0c;训练人工智能模型需要越来越多的硬件资源&#xff0c;例如&#xff0c;利用10万条棋局数据、使用一台PC电脑、完整地训练一次确定性神经网络五子棋模型&#xff0c;需要花费一年半的时间。随着训练数据的增长…

<PLC><HMI><汇川>在汇川HMI画面中,如何为UI设置全局样式?

前言 汇川的HMI软件是使用了Qt来编写的,因此在汇川的HMI程序编写过程,是支持使用qt的样式来自定义部件样式的,即qss格式。 概述 汇川的软件本身提供三个系统的style样式,我们可以直接使用,但是,如果系统提供的样式不符合你的需求,那么你可以对其进行修改,或者自己新建…

进程间通信与线程间通信的方法汇总

目录 一、进程间通信机制 管道(pipe)&#xff1a; 命名管道(FIFO)&#xff1a; 消息队列(MQ)&#xff1a; 信号量(semaphore)&#xff1a; 共享内存(shared memory)&#xff1a; 信号(signal)&#xff1a; 内存映射(mapped memory)&#xff1a; 内存映射和共享内存的区…

NFTScan 正式上线 ERC404 浏览器和 NFT API 数据服务

近日&#xff0c;NFTScan 团队正式对外发布了 ERC404 浏览器&#xff0c;将为 ERC404 生态的 NFT 开发者和用户提供简洁高效的 NFT 数据搜索查询服务。NFTScan 作为全球领先的 NFT 数据基础设施服务商&#xff0c;帮助用户更方便地访问和分析 ERC404 相关的 NFT 数据&#xff0…

git使用总结

概述 简介 Git是一种代码托管技术&#xff0c;很多代码托管平台也是基于Git来实现的。 Git可以帮我们做到很多的事情&#xff0c;比如代码的版本控制&#xff0c;分支管理等。 网址 git官网&#xff1a;https://git-scm.com/ 版本控制系统【VCS】 可以完整保存项目的快照&#…

力扣Hot100-543二叉树的直径

给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5] 输出&a…

【Vue】权限控制

权限管理 分类&#xff1a; 页面权限功能(按钮)权限接口权限 vue3-element-admin 的实现方案 一般我们在业务中将 路由可以分为两种&#xff0c;constantRoutes 和 asyncRoutes。 constantRoutes&#xff1a; 代表那些不需要动态判断权限的路由&#xff0c;如登录页、404(或…

Skywalking 入门与实战

一 什么是 Skywalking? Skywalking 时一个开源的分布式追踪系统&#xff0c;用于检测、诊断和优化分布式系统的功能。它可以帮助开发者和运维人员深入了解分布式系统中各个组件之间的调用关系、性能瓶颈以及异常情况&#xff0c;从而提供系统级的性能优化和故障排查。 1.1 为…