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

手写序列化与反序列化

目录

前言

一、核心设计目标

二、协议基础:分隔符与编码解码

1. 关键常量

2. 编码函数 Encode:生成可传输的完整报文

3. 解码函数 Decode:从流数据中提取完整报文

三、结构化数据载体:Request与Response类

1. Request类:请求数据封装

2. Response类:响应数据封装

四、对象创建:Factory工厂类

核心接口

五、两种协议的对比与切换

六、核心作用与应用场景

总结


前言

本章的主要目的是体会具体序列化和反序列化的过程。

  • 本质:就是对字符串的处理
  • 实际情况肯定会更复杂,但是序列化与反序列化已经有很多的现成解决方案了。我们只是为了方便理解有一次手写的经历就够了。。
C++
#pragma once
#include <iostream>
#include <memory>
#include <jsoncpp/json/json.h>
// #define SelfDefine 1
namespace Protocol
{
// 问题
// 1. 结构化数据的序列和反序列化
// 2. 还要解决用户区分报文边界 --- 数据包粘报问题
// 讲法
// 1. 自定义协议
// 2. 成熟方案序列和反序列化
// 总结:
// 我们今天定义了几组协议呢??我们可以同时存在多个协议吗???可以// "protocol_code\r\nlen\r\nx op y\r\n" : \r\n 不属于报文的一部分,约定
const std::string ProtSep = " ";
const std::string LineBreakSep = "\r\n";
// "len\r\nx op y\r\n" : \r\n 不属于报文的一部分,约定
std::string Encode(const std::string &message)
{
std::string len = std::to_string(message.size());
std::string package = len + LineBreakSep + message +
LineBreakSep;
return package;
}
// "len\r\nx op y\r\n" : \r\n 不属于报文的一部分,约定
// 我无法保证 package 就是一个独立的完整的报文
// "l
// "len
// "len\r\n
// "len\r\nx
// "len\r\nx op
// "len\r\nx op y
// "len\r\nx op y\r\n"
// "len\r\nx op y\r\n""len
// "len\r\nx op y\r\n""len\n
// "len\r\nx op
// "len\r\nx op y\r\n""len\nx op y\r\n"
// "len\r\nresult code\r\n""len\nresult code\r\n"
bool Decode(std::string &package, std::string *message)
{
// 除了解包,我还想判断报文的完整性, 能否正确处理具有"边界"的报文
auto pos = package.find(LineBreakSep);
if (pos == std::string::npos)
return false;
std::string lens = package.substr(0, pos);
int messagelen = std::stoi(lens);
int total = lens.size() + messagelen + 2 *
LineBreakSep.size();
if (package.size() < total)
return false;
// 至少 package 内部一定有一个完整的报文了!
*message = package.substr(pos + LineBreakSep.size(),
messagelen);
package.erase(0, total);
return true;
}
class Request
{
public:
Request() : _data_x(0), _data_y(0), _oper(0)
{
}
Request(int x, int y, char op) : _data_x(x), _data_y(y),_oper(op)
{
}
void Debug()
{
std::cout << "_data_x: " << _data_x << std::endl;
std::cout << "_data_y: " << _data_y << std::endl;
std::cout << "_oper: " << _oper << std::endl;
}
void Inc()
{
_data_x++;
_data_y++;
}
// 结构化数据->字符串
bool Serialize(std::string *out)
{
#ifdef SelfDefine // 条件编译
*out = std::to_string(_data_x) + ProtSep + _oper +
ProtSep + std::to_string(_data_y);
return true;
#else
Json::Value root;
root["datax"] = _data_x;
root["datay"] = _data_y;
root["oper"] = _oper;
Json::FastWriter writer;
*out = writer.write(root);
return true;
#endif
}
bool Deserialize(std::string &in) // "x op y" [)
{
#ifdef SelfDefine
auto left = in.find(ProtSep);
if (left == std::string::npos)
return false;
auto right = in.rfind(ProtSep);
if (right == std::string::npos)
return false;
_data_x = std::stoi(in.substr(0, left));
_data_y = std::stoi(in.substr(right +
ProtSep.size()));
std::string oper = in.substr(left + ProtSep.size(),right - (left + ProtSep.size()));
if (oper.size() != 1)
return false;
_oper = oper[0];
return true;
#else
Json::Value root;
Json::Reader reader;
bool res = reader.parse(in, root);
if(res)
{
_data_x = root["datax"].asInt();
_data_y = root["datay"].asInt();
_oper = root["oper"].asInt();
}
return res;
#endif
}
int GetX() { return _data_x; }
int GetY() { return _data_y; }
char GetOper() { return _oper; }
private:
// _data_x _oper _data_y
// 报文的自描述字段
// "len\r\nx op y\r\n" : \r\n 不属于报文的一部分,约定
// 很多工作都是在做字符串处理!
int _data_x; // 第一个参数
int _data_y; // 第二个参数
char _oper; // + - * / %
};
class Response
{
public:
Response() : _result(0), _code(0)
{
}
Response(int result, int code) : _result(result),
_code(code)
{
}
bool Serialize(std::string *out)
{
#ifdef SelfDefine
*out = std::to_string(_result) + ProtSep +
std::to_string(_code);
return true;
#else
Json::Value root;
root["result"] = _result;
root["code"] = _code;
Json::FastWriter writer;
*out = writer.write(root);
return true;
#endif
}
bool Deserialize(std::string &in) // "_result _code" [){
#ifdef SelfDefine
auto pos = in.find(ProtSep);
if (pos == std::string::npos)
return false;
_result = std::stoi(in.substr(0, pos));
_code = std::stoi(in.substr(pos + ProtSep.size()));return true;
#else
Json::Value root;
Json::Reader reader;
bool res = reader.parse(in, root);
if(res)
{
_result = root["result"].asInt();
_code = root["code"].asInt();
}
return res;
#endif
}
void SetResult(int res) { _result = res; }
void SetCode(int code) { _code = code; }
int GetResult() { return _result; }
int GetCode() { return _code; }
private:
// "len\r\n_result _code\r\n"
int _result; // 运算结果
int _code; // 运算状态
};
// 简单的工厂模式,建造类设计模式
class Factory
{
public:
std::shared_ptr<Request> BuildRequest()
{
std::shared_ptr<Request> req =
std::make_shared<Request>();
return req;
}
std::shared_ptr<Request> BuildRequest(int x, int y, charop)
{
std::shared_ptr<Request> req =
std::make_shared<Request>(x, y, op);
return req;
}
std::shared_ptr<Response> BuildResponse()
{
std::shared_ptr<Response> resp =
std::make_shared<Response>();
return resp;
}
std::shared_ptr<Response> BuildResponse(int result, intcode)
{
std::shared_ptr<Response> req =
std::make_shared<Response>(result, code);
return req;
}
};
}

一、核心设计目标

  1. 解决结构化数据传输:将Request(请求)、Response(响应)这类内存中的结构化数据,转换为可网络传输的字符串(序列化),反之亦然(反序列化)。
  2. 解决粘包 / 拆包问题:通过自定义协议格式,让接收方准确识别每个报文的边界,避免数据混淆。
  3. 支持多协议灵活切换:通过条件编译(SelfDefine宏),可在 “自定义简单协议” 和 “JSON 协议” 之间切换。

二、协议基础:分隔符与编码解码

这是解决粘包 / 拆包的核心,定义了报文的统一格式。

1. 关键常量
  • ProtSep = " ":结构化数据内部的字段分隔符(仅自定义协议用)。
  • LineBreakSep = "\r\n":报文边界分隔符,用于区分 “长度字段” 和 “有效载荷”,且不属于有效数据。
2. 编码函数 Encode:生成可传输的完整报文
  • 功能:将业务数据(message,如序列化后的请求 / 响应字符串)包装成符合协议的完整报文。
  • 格式:长度字符串 + \r\n + 业务数据 + \r\n
    • 示例:业务数据为"3 + 5"(长度 8),编码后为"8\r\n3 + 5\r\n"
  • 核心作用:给业务数据添加 “长度头” 和 “边界标识”,让接收方知道何时读完一个完整报文。
3. 解码函数 Decode:从流数据中提取完整报文
  • 功能:从接收的流式数据(package,可能包含多个报文或不完整报文)中,提取出一个完整的业务数据(message)。
  • 流程:
    1. 查找第一个\r\n,提取前面的 “长度字符串” 并转换为整数messagelen
    2. 计算完整报文的总长度(长度字符串长度 + 业务数据长度 + 2 个\r\n长度)。
    3. 若当前流数据长度不足总长度,返回false(报文不完整,等待后续数据)。
    4. 若长度足够,提取业务数据,从流数据中删除已提取的完整报文,返回true
  • 核心作用:准确拆分流式数据中的完整报文,彻底解决粘包 / 拆包问题。

三、结构化数据载体:RequestResponse

封装通信的业务数据,提供序列化(内存→字符串)和反序列化(字符串→内存)接口。

1. Request类:请求数据封装
  • 用途:客户端向服务端发送的计算请求,包含两个运算数和运算符。
  • 成员变量:
    • _data_x:第一个运算数(如 3)。
    • _data_y:第二个运算数(如 5)。
    • _oper:运算符(如+)。
  • 核心接口:
    • Serialize:将成员变量转换为字符串(两种协议可选)。
      • 自定义协议:"3 + 5"(用ProtSep分隔字段)。
      • JSON 协议:{"datax":3,"datay":5,"oper":"+"}(可读性强,支持复杂结构)。
    • Deserialize:将字符串解析为成员变量,与序列化格式对应。
    • GetX/GetY/GetOper:获取成员变量的接口(封装性)。
2. Response类:响应数据封装
  • 用途:服务端向客户端返回的处理结果,包含计算结果和状态码。
  • 成员变量:
    • _result:运算结果(如 8)。
    • _code:状态码(0 = 成功,非 0 = 错误,如 1 = 除零错误)。
  • 核心接口:
    • Serialize:将结果和状态码转换为字符串(自定义协议:"8 0";JSON 协议:{"result":8,"code":0})。
    • Deserialize:将字符串解析为成员变量。
    • SetResult/SetCode:设置结果和状态码(服务端用)。

四、对象创建:Factory工厂类

采用简单工厂模式,统一管理RequestResponse对象的创建,避免直接new导致的耦合。

核心接口
  • BuildRequest():创建默认Request对象(运算数和运算符为空)。
  • BuildRequest(int x, int y, char op):创建带初始值的Request对象。
  • BuildResponse():创建默认Response对象(结果和状态码为 0)。
  • BuildResponse(int result, int code):创建带初始结果和状态码的Response对象。
  • 返回值:std::shared_ptr智能指针,自动管理对象生命周期,避免内存泄漏。

五、两种协议的对比与切换

通过#define SelfDefine 1宏控制协议类型,灵活适配不同场景:

特性自定义协议(SelfDefine=1JSON 协议(默认)
数据格式简洁字符串(如"3 + 5"标准 JSON 字符串
可读性
扩展能力(如加字段)差(需修改字符串解析逻辑)强(JSON 自动兼容)
传输效率高(字节少)略低(带 JSON 格式字符)
适用场景简单数据、对效率要求高复杂数据、需调试

六、核心作用与应用场景

这套模块是网络通信的 “数据传输标准”,在之前的 TCP 计算器项目中:

  1. 客户端:Request序列化→Encode编码→发送给服务端。
  2. 服务端:接收数据→Decode解码→Request反序列化→处理计算→Response序列化→Encode编码→返回客户端。
  3. 客户端:接收响应→Decode解码→Response反序列化→打印结果。

总结

该模块通过 “协议编码解码 + 结构化数据序列化 + 工厂模式”,完整解决了网络通信中 “数据怎么传”“边界怎么分”“对象怎么创建” 三个核心问题,代码解耦性强、灵活可扩展,可直接复用在各类 C++ TCP/UDP 通信项目中。

http://www.dtcms.com/a/585263.html

相关文章:

  • T41NQ/T41N高性能低功耗SOC芯片 软硬件资料T41NQ适用于各种AIoT应用,适用于智能安防、智能家居,机器视觉等领域方案
  • 购物网站建设要求用wordpress改
  • vector 底层模拟实现(上):核心机制全解析 + 迭代器失效深度剖析
  • mysql内置函数——了解常用的函数
  • 网站建设步骤ppt一个企业seo网站的优化流程
  • 技术演进中的开发沉思-178 JSP :前世今生(下)
  • 做网站学什么软件网页美工实例教程
  • 深入理解 Spring Boot Actuator:构建可观测性与运维友好的应用
  • 现代C++的AI革命:C++20/C++23核心特性解析与实战应用
  • 【数据结构】单链表的经典算法题
  • 网站优化要用什么软件做公司网站哪家好
  • 【DaisyUI】select 和 dropdown 怎么选择?
  • 如何进行oracle提权?
  • K8s API Server 核心解析:集群的“中枢神经”与功能全解
  • 简单两步将你的python转成exe格式
  • 混合澄清槽在氧化铜矿石湿法萃取中的应用
  • Vue3 + TypeScript学习
  • GitHub Action工作流语法
  • 动态效果网站建设技术广东省建筑工程信息网
  • cpp_list
  • rk3588上用rk_mpi_vi_test与ffmpeg实战
  • Rust 练习册 :Queen Attack与国际象棋逻辑
  • CSS学习
  • 使用V4L2工具验证RK3588平台视频设备节点数据有效性
  • Rust 练习册 :Protein Translation与生物信息学
  • 网站开发课程知识点总结图片自动生成器
  • 【STL——常用遍历与查找算法】
  • 牛客网华为在线编程题
  • 29网站建设全部400网站总机 阿里云
  • 第四章 依赖项属性