序列化和反序列化
📟作者主页:慢热的陕西人
🌴专栏链接:Linux
📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言
本博客主要内容网络中比较重要的一个部分序列化和反序列化
文章目录
1.序列化和反序列化的概念
- 序列化:是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化过程中,对象内部的数据会被转换成一个字节序列,可以在网络上进行传输,或者可以存储到本地文件或数据库中。这样,即使在程序关掉之后,这个对象的状态就可以被保存下来。
- 反序列化:是序列化操作的逆过程,将一系列字节(或者其他类型的数据流)集合恢复为对象的过程。这意味着,我们可以将在序列化过程中生成的字节序列,重新转换回原来的对象,所有的数据成员和内部状态都能完全恢复。
序列化和反序列化常常用于在不同系统、服务之间传输数据。例如,在分布式系统中,我们经常需要在网络上传输对象数据;又或者,我们常常需要将对象数据持久化到磁盘,以便将来使用。序列化和反序列化使得这些操作成为了可能。
2.json
2.1如何在Linux上配置jsoncpp
更新yum仓库:首先,请确保您的yum仓库已更新。
sudo yum update
安装jsoncpp库:使用yum安装jsoncpp库。
sudo yum install jsoncpp-devel
编写代码:编写您的C++代码并包含jsoncpp头文件。
#include <iostream> #include <jsoncpp/json/json.h> int main() { // Your code here return 0; }
编译您的代码:使用jsoncpp库进行编译。
g++ your_code.cpp -o output_file_name -ljsoncpp
这会将jsoncpp库链接到您的可执行文件中。
运行您的程序:执行您的可执行文件以运行程序。
./output_file_name
2.2使用方法(案例)
反序列化:从JSON读取数据
反序列化是指将JSON格式的字符串解析为C++中的数据结构(例如,
Json::Value
对象)。这允许程序以编程方式访问JSON数据中的各个元素。假设我们有一个JSON文件
example.json
,内容如下:jsonCopy code{ "name": "John", "age": 25, "is_student": false }
以下代码展示了如何读取这个文件并访问它的内容:
cppCopy code#include <json/json.h> #include <iostream> #include <fstream> int main() { Json::Value root; // 将会包含根节点 std::ifstream file("example.json"); // 打开文件 file >> root; // 解析JSON并存储在root中 // 访问解析后的数据 std::string name = root["name"].asString(); int age = root["age"].asInt(); bool isStudent = root["is_student"].asBool(); std::cout << "Name: " << name << ", Age: " << age << ", Is student: " << isStudent << std::endl; return 0; }
这段代码首先定义了一个
Json::Value
对象root
,用于存储解析后的JSON数据。然后,它打开example.json
文件,使用>>
操作符读取并解析JSON数据到root
中。最后,通过访问root
对象的成员,以特定类型(asString
、asInt
、asBool
等)获取JSON中的数据。序列化:写入JSON数据
序列化是指将C++数据结构转换为JSON格式的字符串。这对于生成JSON数据,以便于存储或网络传输是非常有用的。
以下代码展示了如何创建一个
Json::Value
对象,并将其转换为JSON字符串:cppCopy code#include <json/json.h> #include <iostream> #include <fstream> int main() { Json::Value root; // 创建一个根节点 // 向root添加数据 root["name"] = "Alice"; root["age"] = 30; root["is_student"] = true; // 序列化:将Json::Value转换为字符串 Json::StreamWriterBuilder builder; builder["commentStyle"] = "None"; builder["indentation"] = " "; // 美化输出 std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter()); // 写入文件 std::ofstream outputFile("output.json"); writer->write(root, &outputFile); return 0; }
在这个示例中,我们首先创建一个空的
Json::Value
对象root
,然后向其添加各种类型的数据。使用Json::StreamWriterBuilder
和Json::StreamWriter
,我们将root
对象序列化为一个美化后的JSON字符串,并写入到output.json
文件中。通过这两个示例,你应该能够理解在C++中使用JsonCpp进行序列化和反序列化的基本过程。
3.网络版计算器
3.1简单实现一个协议
我们简单实现一个协议感受一下,什么是协议,以及如何序列化和反序列化
我们这段代码定义了一个网络通信中使用的简单协议,目的是在网络版本的计算机程序中传输请求和回应信息。它包括两个主要部分:请求(
Request
)和响应(Response
)。每个部分都实现了序列化和反序列化功能,用于在结构体和字符串之间转换数据,以便能够通过网络进行传输。使用了Util.hpp
中的工具函数来辅助实现这些功能。下面是对每个类和其功能的详细解释:共同的部分
- 包含了
string
头文件,用于使用字符串。- 使用
namespace protocol_ns
来封装定义的协议类,以防止命名冲突。- 定义了一个分隔符
SEP
,用于序列化和反序列化时分隔不同的数据部分。Request类
代表一个请求,包含以下主要部分:
- 两个操作数(
_x
,_y
)和一个操作符(_op
),类型分别为int
和char
。- 构造函数有两种形式:一个是默认构造函数,另一个接收两个操作数和一个操作符作为参数。
Serialize
函数:将请求的数据转换成字符串形式。通过将操作数转换为字符串并用定义的分隔符和操作符拼接起来实现。Deserialize
函数:将字符串形式的数据转换回请求的结构体形式。首先使用Util::StringSplit
函数根据分隔符拆分字符串,然后检查格式是否正确,并将拆分得到的字符串转换回相应的数据类型。Response类
代表一个响应,包含以下主要部分:
- 一个结果(
_result
)和一个错误码(_code
),类型都是int
。- 构造函数同样有两种形式:一个是默认构造函数,另一个接收结果和错误码作为参数。
Serialize
函数:将响应的数据转换成字符串形式。通过将结果和错误码转换为字符串并用分隔符拼接起来实现。Deserialize
函数:将字符串形式的数据转换回响应的结构体形式。同样使用Util::StringSplit
函数根据分隔符拆分字符串,然后检查格式是否正确,并将拆分得到的字符串转换回相应的数据类型。
/*给网络版本的计算机指定协议*/
#pragma once
#include<string>
#include<cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include"Util.hpp"
using namespace std;
namespace protocol_ns
{
#define SEP " " //定义分隔符
#define SEP_LEN strlen(SEP)
#define HEADER_SEP "\r\n"
#define HEADER_SEP_LEN strlen(HEADER_SEP)
// "10 + 20" => "7"\r\n""10 + 20"\r\n
string AddHeader(const string str)
{
string s = to_string(str.size());
s += HEADER_SEP;
s += str;
s += HEADER_SEP;
return s;
}
string RemoveHeader(const string str, int len)
{
string res = str.substr(str.size() - HEADER_SEP_LEN - len, len);
return res;
}
int ReadPackage(int sock, string &inbuffer, string *package)
{
char buffer[1024];
ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
//这里有问题
if(s <= 0)
return -1; //-1表示未读到
buffer[s] = 0;
inbuffer += buffer;
auto pos = inbuffer.find(HEADER_SEP);
if(pos == string::npos)
{
return 0;
}
string lenstr = inbuffer.substr(0, pos);
int len = Util::ToInt(lenstr);
int targetPackageLen = lenstr.size() + len + 2 * HEADER_SEP_LEN;
if(inbuffer.size() < targetPackageLen)
return 0;
*package = inbuffer.substr(0, targetPackageLen);
inbuffer.erase(0, targetPackageLen);
return len; //返回的是表达式的长度
}
//都要提供序列化和反序列化的功能
//负责请求的协议
class Request
{
public:
Request()
{}
Request(int x, int y, char op):_x(x), _y(y), _op(op)
{}
//用于将结构体转换为字符串:序列化
bool Serialize(string *outstr)
{
//1.清空返回参数
*outstr = "";
//2.转换参数类型为字符串
string x_string = to_string(_x);
string y_string = to_string(_y);
//合并 x_ sep op_ sep y_
*outstr = x_string + SEP + _op + SEP + y_string;
}
//用于将字符串转换为结构体:反序列化
bool Deserialize(const string &instr)
{
//1.使用Util内部提供的StringSplit函数进行完成反序列化
vector<string> result;
Util::StringSplit(instr, SEP, &result);
//2.检测是否合法
if(result.size() != 3) return false;
if(result[1].size() != 1) return false;
//3.取数据
_x = Util::ToInt(result[0]);
_y = Util::ToInt(result[2]);
_op = result[1][0];
return true;
}
~Request()
{
}
public:
int _x; //操作数
int _y; //操作数
char _op; //操作符
};
//负责回答的协议
class Response
{
public:
Response()
{}
Response(int result, int code):_result(result), _code(code)
{}
//序列化
bool Serialize(string *outstr)
{
//1.清空
*outstr = "";
//2.构建字串
string res_string = to_string(_result);
string code_string = to_string(_code);
//3.合并
*outstr = res_string + SEP + code_string;
}
//反序列化
bool Deserialize(const string &instr)
{
//1.拆分
vector<string> result;
Util::StringSplit(instr, SEP, &result);
//2.检测是否合法
if(result.size() != 2) return false;
//3.取数据
_result = Util::ToInt(result[0]);
_code = Util::ToInt(result[1]);
return true;
}
~Response()
{}
public:
int _result; //结果
int _code; //错误码
};
}
3.2如何确保从网络中收到的消息是完整的
在制定协议的过程中,确保从网络中收到的字符串消息完整性的一种常用方法是使用消息长度字段。具体来说,协议的设计可以在消息的开头添加一个固定长度的字段,用来表示整个消息的长度。接收端首先读取这个长度字段,然后再根据该长度读取相应数量的数据,直到接收到完整的消息。
这种方法确保了接收端能够知道消息的长度,并能够根据长度正确地接收完整的消息,从而避免了接收到部分消息或者不完整消息的情况。同时,为了防止恶意用户篡改消息长度字段,可以对消息进行加密或者使用其他安全措施来保护消息的完整性和安全性。
#define SEP " " //定义分隔符
#define SEP_LEN strlen(SEP)
#define HEADER_SEP "\r\n"
#define HEADER_SEP_LEN strlen(HEADER_SEP)
// "10 + 20" => "7"\r\n""10 + 20"\r\n
string AddHeader(const string str)
{
string s = to_string(str.size());
s += HEADER_SEP;
s += str;
s += HEADER_SEP;
return s;
}
string RemoveHeader(const string str, int len)
{
string res = str.substr(str.size() - HEADER_SEP_LEN - len, len);
return res;
}
int ReadPackage(int sock, string &inbuffer, string *package)
{
char buffer[1024];
ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
//这里有问题
if(s <= 0)
return -1; //-1表示未读到
buffer[s] = 0;
inbuffer += buffer;
auto pos = inbuffer.find(HEADER_SEP);
if(pos == string::npos)
{
return 0;
}
string lenstr = inbuffer.substr(0, pos);
int len = Util::ToInt(lenstr);
int targetPackageLen = lenstr.size() + len + 2 * HEADER_SEP_LEN;
if(inbuffer.size() < targetPackageLen)
return 0;
*package = inbuffer.substr(0, targetPackageLen);
inbuffer.erase(0, targetPackageLen);
return len; //返回的是表达式的长度
}
3.3用json
修改我们的协议
如果要使用我们自己实现的协议的话,只需要加个
#define MYPROT
/*给网络版本的计算机指定协议*/ #pragma once #include <string> #include <cstring> #include <jsoncpp/json/json.h> #include <sys/types.h> #include <sys/socket.h> #include "Util.hpp" using namespace std; namespace protocol_ns { #define SEP " " //定义分隔符 #define SEP_LEN strlen(SEP) #define HEADER_SEP "\r\n" #define HEADER_SEP_LEN strlen(HEADER_SEP) // "10 + 20" => "7"\r\n""10 + 20"\r\n string AddHeader(const string str) { string s = to_string(str.size()); s += HEADER_SEP; s += str; s += HEADER_SEP; return s; } string RemoveHeader(const string str, int len) { string res = str.substr(str.size() - HEADER_SEP_LEN - len, len); return res; } int ReadPackage(int sock, string &inbuffer, string *package) { char buffer[1024]; ssize_t s = recv(sock, buffer, sizeof(buffer), 0); //这里有问题 if(s <= 0) return -1; //-1表示未读到 buffer[s] = 0; inbuffer += buffer; auto pos = inbuffer.find(HEADER_SEP); if(pos == string::npos) { return 0; } string lenstr = inbuffer.substr(0, pos); int len = Util::ToInt(lenstr); int targetPackageLen = lenstr.size() + len + 2 * HEADER_SEP_LEN; if(inbuffer.size() < targetPackageLen) return 0; *package = inbuffer.substr(0, targetPackageLen); inbuffer.erase(0, targetPackageLen); return len; //返回的是表达式的长度 } //都要提供序列化和反序列化的功能 //负责请求的协议 class Request { public: Request() {} Request(int x, int y, char op):_x(x), _y(y), _op(op) {} //用于将结构体转换为字符串:序列化 bool Serialize(string *outstr) { //1.清空返回参数 *outstr = ""; #ifdef MYPROT //2.转换参数类型为字符串 string x_string = to_string(_x); string y_string = to_string(_y); //合并 x_ sep op_ sep y_ *outstr = x_string + SEP + _op + SEP + y_string; #else Json::Value root; root["x"] = _x; root["y"] = _y; root["op"] = _op; //Writer:是用来进行序列化的 struct -> string Json::StyledWriter writer; *outstr = writer.write(root); #endif } //用于将字符串转换为结构体:反序列化 bool Deserialize(const string &instr) { #ifdef MYPROT //1.使用Util内部提供的StringSplit函数进行完成反序列化 vector<string> result; Util::StringSplit(instr, SEP, &result); //2.检测是否合法 if(result.size() != 3) return false; if(result[1].size() != 1) return false; //3.取数据 _x = Util::ToInt(result[0]); _y = Util::ToInt(result[2]); _op = result[1][0]; #else Json::Value root; Json::Reader reader; reader.parse(instr, root); _x = root["x"].asInt(); _y = root["y"].asInt(); _op = root["op"].asInt(); #endif return true; } ~Request() { } public: int _x; //操作数 int _y; //操作数 char _op; //操作符 }; //负责回答的协议 class Response { public: Response() {} Response(int result, int code):_result(result), _code(code) {} //序列化 bool Serialize(string *outstr) { //1.清空 *outstr = ""; #ifdef MYPROT //2.构建字串 string res_string = to_string(_result); string code_string = to_string(_code); //3.合并 *outstr = res_string + SEP + code_string; #else Json::Value root; root["result"] = _result; root["code"] = _code; Json::StyledWriter writer; *outstr = writer.write(root); #endif } //反序列化 bool Deserialize(const string &instr) { #ifdef MYPROT //1.拆分 vector<string> result; Util::StringSplit(instr, SEP, &result); //2.检测是否合法 if(result.size() != 2) return false; //3.取数据 _result = Util::ToInt(result[0]); _code = Util::ToInt(result[1]); #else Json::Value root; Json::Reader reader; reader.parse(instr, root); _result = root["result"].asInt(); _code = root["code"].asInt(); #endif return true; } ~Response() {} public: int _result; //结果 int _code; //错误码 }; }
到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正