当前位置: 首页 > article >正文

【计算机网络学习之路】序列化,反序列化和初识协议

文章目录

  • 前言
  • 一. 序列化和反序列化
    • 1.自己实现
    • 2. JSON
  • 二. 初识协议
  • 结束语

前言

本系列文章是计算机网络学习的笔记,欢迎大佬们阅读,纠错,分享相关知识。希望可以与你共同进步。

本篇博文讲解应用层的序列化和反序列化,还有见一下简单的应用层传输协议

一. 序列化和反序列化

在前篇TCP和UDP服务器编写时,业务只是简单的echo客户端发送的数据,但实际生活中,要传输的数据往往复杂的多。使用结构体或类可以保存更多数据,但传输过程中,可能会遇到网络通信两端的操作系统不同,结构体/类的大小不同,内存对齐策略不一致等问题。所以网络传输十分不建议传输结构体或类
这时,序列化和反序列化诞生了。

序列化通俗来说,就是将结构体/类转化为字符串;而反序列化就是将字符串转化为结构体

序列化最重要的作用:在传递和保存对象时,保证对象的完整性和可传递性等问题。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。

反序列化最重要的作用:根据字节流中保存的对象状态及描述信息,通过反序列化重建对象

核心作用就是对象状态的保存和重建。(整个过程核心点就是字节流所保存的对象状态及描述信息

1.自己实现

案例

假如我们现在要实现一个网络版本的计算器
我们把客户端发送的数据称为需求(Request),服务器返回的数据叫做响应(Responce)
此案例的需求有三个变量:数字x,数字y,操作数op
响应有两个变量:结果result,结果码code
需求和响应都需要有序列化反序列化两个功能函数
代码如下:

使用分割符方便Request提取变量,分隔符——空格
1+1 =>(添加分隔符) 1 + 1


#define SEP " " //分隔符
#define SEP_LEN strlen(SEP)
// 请求
class Request
{
public:
    Request() {}

    // 序列化 结构体=>字符串
    bool Serialize(std::string *outStr)
    {
        *outStr = "";
        std::string x_string = std::to_string(_x);
        std::string y_string = std::to_string(_y);
        //添加分隔符
        *outStr = x_string + SEP + _op + SEP + y_string;
        return true;
    }
    // 反序列化 字符串=>结构体
    bool Deserialize(const std::string &str)
    {
        std::vector<std::string> result;
        Util::StringSplit(str, SEP, &result);
        //必须提取出三个变量
        if (result.size() != 3)
            return false;
            
        _x = atoi(result[0]);
        _op = result[1][0];
        _y = atoi(result[2]);
        return true;
    }
public:
    int _x;
    int _y;
    char _op;
};

// 响应
class Responce
{
public:
    Responce() : _result(0), _code(0)
    {
    }
    // 序列化 结构体->字符串
    bool Serialize(std::string *outStr)
    {
        *outStr = "";
        std::string result_string = std::to_string(_result);
        std::string code_string = std::to_string(_code);
        *outStr = result_string + SEP + code_string;
        return true;
    }
	//反序列化 字符串->结构体
    bool Deserialize(const std::string &str)
    {
        std::vector<std::string> result;
        Util::StringSplit(str, SEP, &result);
        if (result.size() != 2)
            return false;

        _result = atoi(result[0]);
        _code = atoi(result[1]);
        return true;
    }
public:
    int _result;
    int _code;
};

2. JSON

序列化一般我们不自己操作,可以使用别的封装好的序列化,比如:JSON,ProtocolBuffer,FlatBuffer,DIMBIN

本篇文章介绍json的使用
JSON 的语法规则总结起来有:

  • 数组(Array)用方括号(“[]”)表示。
  • 对象(0bject)用大括号(“{}”)表示。
  • 名称/值对(name/value)组合成数组和对象。
  • 名称(name)置于双引号中,值(value)有字符串、数值、布尔值、null、对象和数组。
  • 并列的数据之间用逗号(“,”)分隔

序列化后可视效果也较好,比如:

{
	"x":10,
	"y":22,
	"op":*
}

代码:

// 请求
class Request
{
public:
    Request() {}

    // 结构体=>字符串
    bool Serialize(std::string *outStr)
    {
        // Value,一种万能对象,接收任意的kv类型
        Json::Value root;
        root["x"] = _x;
        root["y"] = _y;
        root["op"] = _op;
        // 序列化
        Json::StyledWriter writer;
        *outStr = writer.write(root);
        return true;
    }
    // 字符串=>结构体
    bool Deserialize(const std::string &str)
    {
        Json::Value root;
        // 反序列化
        Json::Reader reader;
        reader.parse(str, root);
		//提取变量
        _x = root["x"].asInt();
        _y = root["y"].asInt();
        _op = root["op"].asInt();
        return true;
    }
public:
    int _x;
    int _y;
    char _op;
};
// 响应
class Responce
{
public:
    Responce() : _result(0), _code(0)
    {}
    // 结构体->字符串
    bool Serialize(std::string *outStr)
    {
        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;

        // 序列化
        Json::StyledWriter writer;
        *outStr = writer.write(root);
        return true;
    }

    bool Deserialize(const std::string &str)
    {
        Json::Value root;
        //反序列化
        Json::Reader reader;
        reader.parse(str, root);
		//提取变量
        _result = root["result"].asInt();
        _code = root["code"].asInt();
        return true;
    }
public:
    int _result;
    int _code;
};

二. 初识协议

在网络通信中,客户端,服务器收发数据其实是如下这样的

在这里插入图片描述

我们调用的recv,send等接口,只是将我们定义的缓冲区的数据拷贝到TCP的缓冲区,或者将数据从TCP缓冲区拷贝到上层
TCP是面向字节流的,可以认为,数据是一个字节一个字节传输的
如果,客户端发送一个hello,recv接口会将发送缓冲区的数据一次性拷贝到上层,但可能此时通过网络传输,只传送了hel,服务器的接受缓冲区只有hel,此时recv并没有读取到完整报文,并且我们不知道什么时候读到了完整报文
协议就是为了解决这类问题而诞生的

基于本次网络计算机的案列,简单设计的协议比如,添加长度和\r\n报头
比如:“1 + 1” =>“5"”\r\n"“1 + 1"”\r\n"

解析:通过找到第一个\r\n,获取有效载荷(1 + 1)的长度,再根据长度提取有效载荷

代码如下:

#define HEADER_SEP "\r\n" // 报头分隔符
#define HEADER_SEP_LEN strlen(HEADER_SEP)
// 读取数据,并尝试提取一个完整报文
// 完整报文:"有效载荷长度""\r\n""有效载荷""\r\n"
//inbuffer保存所有读取的数据,package是一个完整报文,需要输出
int ReadPackage(int sock, std::string &inbuffer, std::string *package)
{
    // 读数据
    char buffer[1024];
    int n = recv(sock, buffer, sizeof(buffer) - 1, 0);
    if (n > 0)
        buffer[n] = '\0';
    else if (n == 0)//写端关闭
        return -1;
    else if (n < 0)//读取异常
        return -2;
    //将本次读取的数据保存
    inbuffer += buffer;
	//开始查找报头分隔符
    size_t start = 0;
    auto pos = inbuffer.find(HEADER_SEP, start);
    if (pos == std::string::npos)
        return 0; //第一个分隔符都没有,不是完整报文
	//提取长度
    std::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;
}

结束语

本篇博客到此结束,感谢看到此处。
欢迎大家纠错和补充
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述


http://www.kler.cn/news/148242.html

相关文章:

  • ChromeDriver最新版本下载与安装方法
  • 【面试HOT200】滑动窗口篇
  • HNU练习七 字符串编程题7. 机器人游戏
  • DS图—图的最短路径/Dijkstra算法【数据结构】
  • 【数据结构/C++】线性表_顺序表的基本操作
  • Windows11通用快捷键集合
  • 嵌入式开发DDR的选择
  • python-面试重点问题
  • 【深度学习】CNN中pooling层的作用
  • 使用new Vue()的时候发生了什么?
  • Ajax技
  • 解锁领先的有限元分析软件ABAQUS:不同版本功能特点及价格
  • 月底年末如何成交?速看!外贸销冠都在用的催单技巧,让成交量飙升!
  • 【JavaEE初阶】——Linux 基本使用和 web 程序部署(下)
  • H5(uniapp)中使用echarts
  • 【办公软件】XML格式文件怎么转Excel表格文件?
  • C#学习相关系列之数组---常用方法使用(二)
  • C#,《小白学程序》第十六课:随机数(Random)第三,正态分布的随机数的计算方法与代码
  • SpectralGPT: Spectral Foundation Model 论文翻译1
  • 【开源】基于Vue.js的高校学生管理系统的设计和实现
  • 【Linux学习笔记】protobuf 基本数据编码
  • 链表OJ--下
  • 31.0/LinkedList/Set/ashSet/ TreeSet/Map/ HashMap/ TreeMap
  • rtsp点播异常出现‘circluar_buffer_size‘ option was set but it is xx
  • c语言练习12周(15~16)
  • 408—电子笔记分享
  • IEEE Fellow 2024名单揭晓:哪些半导体专家值得关注
  • 力扣二叉树--第三十四天
  • 你真的懂人工智能吗?AI真的只是能陪你聊天而已吗?
  • MySQL的Redo Log跟Binlog