序列化和反序列化

序列化和反序列化

📟作者主页:慢热的陕西人

🌴专栏链接:Linux

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

本博客主要内容网络中比较重要的一个部分序列化和反序列化

1.序列化和反序列化的概念

  1. 序列化:是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化过程中,对象内部的数据会被转换成一个字节序列,可以在网络上进行传输,或者可以存储到本地文件或数据库中。这样,即使在程序关掉之后,这个对象的状态就可以被保存下来。
  2. 反序列化:是序列化操作的逆过程,将一系列字节(或者其他类型的数据流)集合恢复为对象的过程。这意味着,我们可以将在序列化过程中生成的字节序列,重新转换回原来的对象,所有的数据成员和内部状态都能完全恢复。

序列化和反序列化常常用于在不同系统、服务之间传输数据。例如,在分布式系统中,我们经常需要在网络上传输对象数据;又或者,我们常常需要将对象数据持久化到磁盘,以便将来使用。序列化和反序列化使得这些操作成为了可能。

2.json

2.1如何在Linux上配置jsoncpp

  1. 更新yum仓库:首先,请确保您的yum仓库已更新。

    sudo yum update
    
  2. 安装jsoncpp库:使用yum安装jsoncpp库。

    sudo yum install jsoncpp-devel
    
  3. 编写代码:编写您的C++代码并包含jsoncpp头文件。

    #include <iostream>
    #include <jsoncpp/json/json.h>
    
    int main() {
        // Your code here
        return 0;
    }
    
  4. 编译您的代码:使用jsoncpp库进行编译。

    g++ your_code.cpp -o output_file_name -ljsoncpp
    

    这会将jsoncpp库链接到您的可执行文件中。

  5. 运行您的程序:执行您的可执行文件以运行程序。

    ./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对象的成员,以特定类型(asStringasIntasBool等)获取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::StreamWriterBuilderJson::StreamWriter,我们将root对象序列化为一个美化后的JSON字符串,并写入到output.json文件中。

通过这两个示例,你应该能够理解在C++中使用JsonCpp进行序列化和反序列化的基本过程。

3.网络版计算器

3.1简单实现一个协议

我们简单实现一个协议感受一下,什么是协议,以及如何序列化和反序列化

我们这段代码定义了一个网络通信中使用的简单协议,目的是在网络版本的计算机程序中传输请求和回应信息。它包括两个主要部分:请求(Request)和响应(Response)。每个部分都实现了序列化和反序列化功能,用于在结构体和字符串之间转换数据,以便能够通过网络进行传输。使用了Util.hpp中的工具函数来辅助实现这些功能。下面是对每个类和其功能的详细解释:

共同的部分

  • 包含了string头文件,用于使用字符串。
  • 使用namespace protocol_ns来封装定义的协议类,以防止命名冲突。
  • 定义了一个分隔符SEP,用于序列化和反序列化时分隔不同的数据部分。

Request类

代表一个请求,包含以下主要部分:

  • 两个操作数(_x, _y)和一个操作符(_op),类型分别为intchar
  • 构造函数有两种形式:一个是默认构造函数,另一个接收两个操作数和一个操作符作为参数。
  • 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; //错误码
    };
}

到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

相关推荐

  1. Unity-序列序列

    2024-03-16 23:46:01       62 阅读
  2. 序列序列

    2024-03-16 23:46:01       34 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-16 23:46:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-16 23:46:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-16 23:46:01       82 阅读
  4. Python语言-面向对象

    2024-03-16 23:46:01       91 阅读

热门阅读

  1. OpenAI Client

    2024-03-16 23:46:01       37 阅读
  2. latex入门笔记

    2024-03-16 23:46:01       36 阅读
  3. springboot项目jwt认证鉴权(企业级实现方案)

    2024-03-16 23:46:01       32 阅读
  4. react的高阶组件怎么用?

    2024-03-16 23:46:01       41 阅读
  5. 安卓UI面试题 51-55

    2024-03-16 23:46:01       42 阅读
  6. Acwing101 --- 最高的牛(差分)

    2024-03-16 23:46:01       38 阅读
  7. MATLAB中的数据类型

    2024-03-16 23:46:01       42 阅读
  8. Redis语法总结

    2024-03-16 23:46:01       58 阅读
  9. 【2024-03-16】蚂蚁金服春招实习笔试三道编程题解

    2024-03-16 23:46:01       57 阅读
  10. python-0008-修改django数据库为mysql

    2024-03-16 23:46:01       35 阅读
  11. libcurl test

    2024-03-16 23:46:01       41 阅读
  12. 【LeetCode】动态规划--题目练习

    2024-03-16 23:46:01       41 阅读