Linux网络编程——序列反序列化

文章目录

    • 0. 前言
    • 1. 认识协议
    • 2. 序列号与反序列化
    • 3. 自定义协议——网络计算器
    • 4. json

本章Gitee仓库:序列反序列化

0. 前言

tcp是面向字节流的,但是如何保证读取的数据是一个完整的报文呢?

管道也是面向字节流,写端写了一大堆的数据,然后读端可能一下子全部读上来了,这一堆数据如何区分,是一个需要考虑的问题

tcp是传输控制协议,它要管:

  1. 什么时候发送
  2. 发送多少数据
  3. 如果出错了如何解决

我们用户在应用层,通过write将数据写到tcp发送缓冲区,然后write直接返回,但是这个数据并不一定发送给对方了,write只是将数据从用户拷贝到内核,而具体什么时候发、怎么发,都是由tcp决定,因为只有tcp才清楚当前网络的状况和接收方的接收能力

tcp是操作系统的网络模块部分,就可以理解为,用户将数据给了操作系统

接收方通过read将接收缓冲区的数据拷贝到用户层的缓冲区,怎么读这些报文是个问题。

所以需要在用户层需要定制协议

1. 认识协议

比如说要实现一个网络计算器,一个算式要包括数据和运算符号,例如1 + 12 * 2

那就可以定义一个结构,规定的每次发送的数据格式:

struct task
{int data1;	//数据1int data2;	//数据2char op;	//运算符
}

然后接收方那边有一个相同的结构体进行接收,然后将数据提取出来即可,然后计算的结果,也要规定格式,例如是否正确,如果不正确是什么原因导致:

struct calc
{int result;	//结果int code;	//退出码
}

以上这种以结构化的方式把约定表达出来,这就是协议。

2. 序列号与反序列化

对于不同的平台struct的大小可能不一样,有的可能会内存对齐,有的又不会,所以不能够直接将结构体对象发送给对方。

例如我们微信聊天的时候:

image-20240218230034206

我们发送的消息不只是消息本身,还携带了发送的数据,谁发送的,以及我们的头像

struct msg
{string info;	//消息string nickname;	//昵称string time;	//时间
}

这里是三个字符串,肯定是不能分开发的,不然消息就混乱了,所以就需要打个包,将这三个字符串转成一个字符串发送,这叫序列化;接收方收到这一个字符串之后,就将这一个字符串再解析成三个字符串,这就叫反序列化

image-20240218231157926

序列反序列化的主要目的就是方便网络进行接收。

所以在定制协议的时候,只需要考虑定制什么样的结构化数据,然后将这个结构化的数据序列化成一个大字符串。

这里面也有一个隐含的问题,就是如何将这个大字符串反序列化,所以我们在发的时候,要设置好标记

3. 自定义协议——网络计算器

网络版本的计算机,就要涉及到数据包的发送,所以这里就需要定制协议。

要将一个结构化的数据转成字符串,就要设置后间隔标志,以便于反序列化:

protocol.hpp:

#pragma once#include<iostream>const std::string blank_space_sep = " ";
const std::string protocol_sep = "\n";//添加报头
std::string Encode(std::string &content)
{std::string package = std::to_string(content.size());package += protocol_sep;package += content;package += protocol_sep;return package;
}//去掉报头
bool Decode(std::string &package, std::string *content)
{//"len"\n"x op y"\nsize_t pos = package.find(protocol_sep);if(pos == std::string::npos)    return false;std::string len_str = package.substr(0, pos);size_t len = std::stoi(len_str);size_t total_len = len_str.size() + len + 2;if(package.size() < total_len)  return false;*content = package.substr(pos+1, len);//移除已提取报文package.erase(0, total_len);return true;
}class Request
{
public:Request(int data1, int data2, char op):data1_(data1),data2_(data2),op_(op){}Request(){}
public://序列化bool Serialize(std::string *out){//有效载荷//x op ystd::string s = std::to_string(data1_);s += blank_space_sep;s += op_;s += blank_space_sep;s += std::to_string(data2_);*out = s;return true;}//反序列化bool Deserialize(const std::string &in){//"data1 op data2"size_t leftpos = in.find(blank_space_sep);if(leftpos == std::string::npos)    return false;std::string part1 = in.substr(0, leftpos);size_t rightpos = in.rfind(blank_space_sep);if(rightpos == std::string::npos)   return false;std::string part2 = in.substr(rightpos+1);if(leftpos+1 != rightpos-1)    return false;op_ = in[leftpos+1];data1_ = std::stoi(part1);data2_ = std::stoi(part2);return true;}void DebugPrint(){std::cout << "receive a request: " << data1_ << op_ << data2_ << std::endl;}
public:int data1_;int data2_;char op_;
};class Response
{
public:Response(int res, int code):result_(res),exitcode_(code){}Response(){}
public://序列化bool Serialize(std::string *out){//有效载荷//"result exitcode"std::string s = std::to_string(result_);s += blank_space_sep;s += std::to_string(exitcode_);*out = s;return true;}//反序列化bool Deserialize(const std::string &in){//"result exitcode"size_t pos = in.find(blank_space_sep);if(pos == std::string::npos)    return false;std::string part1 = in.substr(0, pos);std::string part2 = in.substr(pos+1);result_ = std::stoi(part1);exitcode_ = std::stoi(part2);return true;}void DebugPrint(){std::cout << "ret calc done, result: " << result_ << " exitcode: " << exitcode_ << std::endl;}public:int result_;int exitcode_;
};

这个协议可以自己定,下面稍微讲解一下本次自定义的协议

这里规定,以空格为分隔符:data1 op data2,需要向网络发送的时候,会添加一个报头,格式为"字符串长度"\n报文\n;收到这个请求之后,先将这个大字符串反序列化,提取data1 op data2,当然这里可能解析的不是一个完整的报文,所以需要判断解析的报文不超过原始字符串的长度。

对于计算结果的序列和反序列化,比请求的稍微简单一点,这里就不多说了,代码里面有注释

运行演示:

GIF 2024-2-20 1-15-48

4. json

当然序列反序列化也不用每次我们自己手动去做,我们可以采用json

C++要使用json,是需要安装第三方库的jsoncpp

sudo yum install -y jsoncpp-devel

image-20240220012523958

test.cc:

#include<iostream>
#include<jsoncpp/json/json.h>int main()
{Json::Value js;js["json"] = "js";js["aaa"] = "AAA";Json::Value v1;v1["data1"] = 100;v1["data2"] = 20;v1["op"] = '+';v1["rejs"] = js;    //json里面套json//序列化//Json::FastWriter fw;Json::StyledWriter sw;std::string ser = sw.write(v1);std::cout << ser << std::endl;//反序列化Json::Value v2;Json::Reader r;//参数1:需要反序列化的字符串    参数2:反序列化的结果    参数3:直接缺省先r.parse(ser, v2);int data1 = v2["data1"].asInt();int data2 = v2["data2"].asInt();char op = v2["op"].asInt();Json::Value tmp = v2["rejs"];std::string s1 = tmp["json"].asString();std::string s2 = tmp["aaa"].asString();std::cout << data1 << std::endl;std::cout << data2 << std::endl;std::cout << op << std::endl;std::cout << s1 << std::endl;std::cout << s2 << std::endl;return 0;
}

image-20240220014818730

这样就可以使用条件编译,来选择是用我们自己写的序列反序列化,还是直接使用json

编译的时候加上-D选项,例如要定义#define Myself 1,就可以加上-DMyself=1

#pragma once#include<iostream>
#include<jsoncpp/json/json.h>
//#define Myself 1const std::string blank_space_sep = " ";
const std::string protocol_sep = "\n";//添加报头
std::string Encode(std::string &content)
{std::string package = std::to_string(content.size());package += protocol_sep;package += content;package += protocol_sep;return package;
}//去掉报头
bool Decode(std::string &package, std::string *content)
{//"len"\n"x op y"\nsize_t pos = package.find(protocol_sep);if(pos == std::string::npos)    return false;std::string len_str = package.substr(0, pos);size_t len = std::stoi(len_str);size_t total_len = len_str.size() + len + 2;if(package.size() < total_len)  return false;*content = package.substr(pos+1, len);//移除已提取报文package.erase(0, total_len);return true;
}class Request
{
public:Request(int data1, int data2, char op):data1_(data1),data2_(data2),op_(op){}Request(){}
public://序列化bool Serialize(std::string *out){
#ifdef Myself//有效载荷//x op ystd::string s = std::to_string(data1_);s += blank_space_sep;s += op_;s += blank_space_sep;s += std::to_string(data2_);*out = s;return true;
#elseJson::Value root;root["data1"] = data1_;root["data2"] = data2_;root["op"] = op_;Json::FastWriter fw;*out = fw.write(root);return true;
#endif}//反序列化bool Deserialize(const std::string &in){
#ifdef Myself//"data1 op data2"size_t leftpos = in.find(blank_space_sep);if(leftpos == std::string::npos)    return false;std::string part1 = in.substr(0, leftpos);size_t rightpos = in.rfind(blank_space_sep);if(rightpos == std::string::npos)   return false;std::string part2 = in.substr(rightpos+1);if(leftpos+1 != rightpos-1)    return false;op_ = in[leftpos+1];data1_ = std::stoi(part1);data2_ = std::stoi(part2);return true;
#elseJson::Value root;Json::Reader r;r.parse(in, root);data1_ = root["data1"].asInt();data2_ = root["data2"].asInt();op_ = root["op"].asInt();return true;
#endif}void DebugPrint(){std::cout << "receive a request: " << data1_ << op_ << data2_ << std::endl;}
public:int data1_;int data2_;char op_;
};class Response
{
public:Response(int res, int code):result_(res),exitcode_(code){}Response(){}
public://序列化bool Serialize(std::string *out){
#ifdef Myself//有效载荷//"result exitcode"std::string s = std::to_string(result_);s += blank_space_sep;s += std::to_string(exitcode_);*out = s;return true;
#elseJson::Value root;root["result"] = result_;root["exitcode"] = exitcode_;Json::FastWriter fw;*out = fw.write(root);return true;
#endif}//反序列化bool Deserialize(const std::string &in){
#ifdef Myself//"result exitcode"size_t pos = in.find(blank_space_sep);if(pos == std::string::npos)    return false;std::string part1 = in.substr(0, pos);std::string part2 = in.substr(pos+1);result_ = std::stoi(part1);exitcode_ = std::stoi(part2);return true;
#elseJson::Value root;Json::Reader r;r.parse(in, root);result_ = root["result"].asInt();exitcode_ = root["exitcode"].asInt();return true;
#endif}void DebugPrint(){std::cout << "ret calc done, result: " << result_ << " exitcode: " << exitcode_ << std::endl;}public:int result_;int exitcode_;
};

本章自定义的协议,就是属于OSI七层模型中的表示层

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

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

相关文章

Sora:新一代实时音视频通信框架

一、Sora简介 Sora是一个开源的实时音视频通信框架&#xff0c;旨在提供高效、稳定、可扩展的音视频通信解决方案。它基于WebRTC技术&#xff0c;支持跨平台、跨浏览器的实时音视频通信&#xff0c;并且具备低延迟、高并发、易集成等特点。 --点击进入Sora(一定要科学哦&#x…

机器学习基础(一)理解机器学习的本质

导读&#xff1a;在本文中&#xff0c;将深入探索机器学习的根本原理&#xff0c;包括基本概念、分类及如何通过构建预测模型来应用这些理论。 目录 机器学习 机器学习概念 相关概念 机器学习根本&#xff1a;模型 数据的语言&#xff1a;特征与标签 训练与测试&#xf…

四分位距IQR_ interquartile range

四分位距IQR_ interquartile range 1 IQR&#xff08;Interquartile Range&#xff09;四分位距的含义2 如何计算IQR参考&#xff1a; 1 IQR&#xff08;Interquartile Range&#xff09;四分位距的含义 官方定义&#xff1a; 四分位距&#xff08;interquartile range, IQR&a…

机器学习中梯度下降法的缺点

机器学习中的梯度下降法是一种寻找函数最小值的优化算法&#xff0c;广泛应用于训练各种模型&#xff0c;尤其是在深度学习中。尽管其应用广泛&#xff0c;但梯度下降法也存在一些不可忽视的缺点&#xff1a; 1. 局部最小值和鞍点 局部最小值问题&#xff1a; 对于非凸函数&a…

数据分析 - 机器学习

1&#xff1a;线性回归 线性回归是一种统计技术用于对输出变量与一个或多个输入变量之间的关系进行建模 用外行人的话来说&#xff0c;将其视为通过某些数据点拟合一条线&#xff0c;如下所示 以便在未知数据上进行预测&#xff0c;假设变量之间存在线性关系 点和线之间存在微小…

[ansible] playbook运用

一、复习playbook剧本 --- - name: first play for install nginx #设置play的名称gather_facts: false #设置不收集facts信息hosts: webservers:dbservers #指定执行此play的远程主机组remote_user: root #指定执行此play的用…

python绘制k线图均线图

AAPL.csv 数据文件 Date,Close,Volume,Open,High,Low 06/23/2023,$186.68,53117000,$185.55,$187.56,$185.01 06/22/2023,$187.00,51245330,$183.74,$187.045,$183.67 06/21/2023,$183.96,49515700,$184.90,$185.41,$182.5901 06/20/2023,$185.01,49799090,$184.41,$1…

15-55V输入自动升降压 光伏MPPT自动跟踪充电方案 大功率300瓦

1.MPPT原理--简介 MPPT&#xff0c;全称为Maximum Power Point Tracking&#xff0c;即最大功点跟踪&#xff0c;它是一种通过调节电气模块的工作状态&#xff0c;使光伏板能够输出更多电能的电气系统能够将太阳能电池板发出的直流电有效地贮存在蓄电池中&#xff0c;可有效地…

【蓝桥杯】算法模板题(Floyd算法)

一.弗洛伊德算法 用途&#xff1a;用来求解多源点最短路径问题。 思想&#xff1a;Floyd算法又称为插点法&#xff0c;是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法。 主要步骤&#xff1a; 1&#xff09;初始化&#xff1a;使用邻接矩阵初始化dis…

GitHub仓库文件部署

目录 软件下载和安装 git创建仓库 Github仓库配置 git管理软件配置 Git管理 软件下载和安装 首先需要下载git&#xff0c;以及git管理软件&#xff0c;对其进行安装。 git创建仓库 首先需要创建仓库&#xff0c;在本地仓库文件夹cmd之后输入以下指令创建git仓库文件。 …

小米空气净化器2s使用体验

这个产品最早上市是2017年&#xff0c;我买回来实际上只用了1年就弃用了&#xff0c;性能不行&#xff0c;使用体验也不好。 打算买新的空气净化器&#xff0c;抽空吐槽一下。 这个净化器发售价是899&#xff0c;在当时来说算中下水平的&#xff0c;小米的&#xff0c;有米家…

Spring Boot与LiteFlow:轻量级流程引擎的集成与应用含完整过程

点击下载《Spring Boot与LiteFlow&#xff1a;轻量级流程引擎的集成与应用含完整过程》添加链接描述 1. 前言 本文旨在介绍Spring Boot与LiteFlow的集成方法&#xff0c;详细阐述LiteFlow的原理、使用流程、步骤以及代码注释。通过本文&#xff0c;读者将能够了解LiteFlow的特…

数据分析师SQL面试准备(part1)

1. SQL 万能框架 2. SQL的书写顺序&#xff0c;跟程序真的执行顺序不同 3. 4. 5. 6. 7. case when utilization 8. 9. 10. 11.

OpenHarmony—UIAbility组件间交互(设备内)

UIAbility是系统调度的最小单元。在设备内的功能模块之间跳转时&#xff0c;会涉及到启动特定的UIAbility&#xff0c;该UIAbility可以是应用内的其他UIAbility&#xff0c;也可以是其他应用的UIAbility&#xff08;例如启动三方支付UIAbility&#xff09;。 本章节将从如下场…

多维时序 | Matlab实现LSTM-Mutilhead-Attention长短期记忆神经网络融合多头注意力机制多变量时间序列预测模型

多维时序 | Matlab实现LSTM-Mutilhead-Attention长短期记忆神经网络融合多头注意力机制多变量时间序列预测模型 目录 多维时序 | Matlab实现LSTM-Mutilhead-Attention长短期记忆神经网络融合多头注意力机制多变量时间序列预测模型预测效果基本介绍程序设计参考资料 预测效果 基…

化学空间可视化(chemical space visualization)开源软件ChemPlot的安装及使用

文章目录 前言一、ChemPlot是什么&#xff1f;二、conda环境安装ChemPlot1. 创建conda环境2. 安装chemplot及需要的包3. 检验安装 三、使用步骤1. 化合物数据库可视化使用方法BBBP数据库的t-SNE降维后可视化&#xff1a;BBBP数据库的PCA降维后可视化&#xff1a;BBBP数据库的UM…

shapely 笔记:基本方法

1 线性方法 1.1 object.interpolate(distance[, normalizedFalse]) print(LineString([(0, 0), (0, 1), (1, 1)]).interpolate(1.5)) #POINT (0.5 1)print(LineString([(0, 0), (0, 1), (1, 1)]).interpolate(0.75, normalizedTrue)) #POINT (0.5 1) LineString([(0, 0), (0…

JimuReport积木报表 v1.7.0 变革版本发布,低代码报表设计工具

项目介绍 一款免费的数据可视化报表&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完成报…

从零开始的 dbt 入门教程 (dbt core 开发进阶篇)

引 在上一篇文章中&#xff0c;我们花了专门的篇幅介绍了 dbt 更多实用的命令&#xff0c;那么我们继续按照之前的约定来聊 dbt 中你可能会遇到的疑惑以及有用的概念&#xff0c;如果你是 dbt 初学者&#xff0c;我相信如下知识点一定会对你有极大的帮助&#xff1a; 了解 dbt_…

java基础训练题(2)

一、题目 1. 以下程序输出&#xff08;D&#xff09; public static void main(String[] args) {int num 2;switch (num) {case 1:num;case 2:num;case 3:num;default:num;break;}System.out.println(num);} } A&#xff1a;2 B&#xff1a;3 C&#xff1a;4 D&#xff…