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

Linux网络之序列化和反序列化

目录

序列化和反序列化


上期我们学习了基于TCP的socket套接字编程接口,并实现了一个TCP网络小程序,本期我们将在此基础上进一步延伸学习,实现一个网络版简单计算器。

序列化和反序列化

在生活中肯定有这样一个情景。

上图大家肯定不陌生,上图是群聊机器人发送的一条消息记录,通过这个记录我们可以看到机器人的头像,机器人的昵称,机器人发送的消息内容,机器人发送消息的时间。我上述数据统称为一次发送产生的数据。使用qq的人是很多的,所以上述数据也是很多,为了方便进行管理数据,操作系统会将一次发送的数据进行管理,这就要用到六子真言,“先描述,后组织”。所以操作系统会定义一个结构体,结构体中会定义一些成员表述上述的数据,我们称之为结构化数据。

 图示如下。

总的来说。

序列化就是将结构化的数据转为字符串数据,反序列化就是将字符串数据转为结构化数据。

序列化和反序列化的优点。

  1. 为了应用层网络通信的便捷,因为在网络通信中,字符串更易于传输。
  2.  为了方便上层端口使用结构化数据内部的成员,将应用和网络进行了解耦。比如发送端发送什么类型的数据,接收端就接收什么类型的数据,双方都不用关心结构化数据在网络中是怎么样传送的。

情景一:制造一个网络计算器小程序(不使用序列化和反序列化)。

Sock.hpp

#include <iostream>
#include <sys/types.h>
#include <cerrno>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>using namespace std;class Socket
{
public:// 1.创建套接字static int Sock(){int fd = socket(AF_INET, SOCK_STREAM, 0);if (fd < 0){cout << "socket create error" << errno << endl;exit(1);}return fd;}// 2.绑定IP和端口号static void Bind(int sock, uint16_t port){struct sockaddr_in local;local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = INADDR_ANY;if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0){cout << "bind error" << errno << endl;exit(2);}}// 3.监听连接static void Listen(int sock){if (listen(sock, 5) < 0){cout << "listen error" << errno << endl;exit(3);}}// 4.获取连接static int Accept(int sock){struct sockaddr_in peer;socklen_t len = sizeof(peer);int newfd = accept(sock, (struct sockaddr *)&peer, &len);if (newfd < 0){cout << "accept error" << errno << endl;exit(4);}return newfd;}// 5.发送链接static void Connect(int fd, string ip, uint16_t port){struct sockaddr_in sever;sever.sin_family = AF_INET;sever.sin_addr.s_addr = inet_addr(ip.c_str());sever.sin_port=htons(port);if (connect(fd, (struct sockaddr *)&sever, sizeof(sever)) == 0){cout << "connect success!" << endl;}else{cout << "connect error" << endl;exit(5);}}
};

Sock.hpp封装了socket套接字的相关接口,方便后续进行使用。

Protocol.hpp

#pragma once
#include <iostream>
#include <string>
using namespace std;// 客户端发送请求
typedef struct request
{int x;int y;char op;} request_t;// 服务端做出响应(回应)
typedef struct response
{int code;   // code表示sever端计算的结果是否正确int result; // 表示计算的结果
} response_t;

可以理解为这是我们定制的一个协议,也就是结构化的数据。客户端发送request请求,服务器端发送response回应。 

 CalClient.cc

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Protocol.hpp"
#include "Sock.hpp"
#include <unistd.h>
#include <string.h>using namespace std;int main(int args, char *argv[])
{// 1.创建套接字文件int fd = Socket::Sock();// 2.发送链接Socket::Connect(fd, argv[1], atoi(argv[2]));// 3.发送请求request_t req;memset(&req, 0, sizeof(req));cout << "请输入x: ";cin >> req.x;cout << "请输入y: ";cin >> req.y;cout << "请输入op: ";cin >> req.op;write(fd, &req, sizeof(req));// 写都是输入型参数,读都是输出型参数// 4.获取回应response_t res;memset(&res, 0, sizeof(res));size_t s = read(fd, &res, sizeof(res));if (s == sizeof(res)){cout << "获取到了回应: " << res.code << " " << res.result << endl;}return 0;
}

client客户端,发送要计算的数据,将其打包成为request请求发送给sever服务器端,并接收sever服务器端的回应。 

要注意的是一般情况下write接口中传入输入型参数,read中传入输出型参数。

CalSever.cc

#include <iostream>
#include "Sock.hpp"
#include "Protocol.hpp"
#include <pthread.h>
#include <unistd.h>
#include <string.h>
using namespace std;// 5.提供服务
void *handle(void *args)
{int newfd = *(int *)args;delete (int *)args;// 获取请求request_t req;size_t s = read(newfd, &req, sizeof(req));if (s == sizeof(req)){// 处理请求response_t res = {0, 0};switch (req.op){case '+':res.result = req.x + req.y;break;case '-':res.result = req.x - req.y;break;case '*':res.result = req.x * req.y;break;case '/':if (req.y == 0){res.code = 1;cout << "除零错误" << endl;break;} // 除零错误res.result = req.x / req.y;break;case '%':if (req.y == 0){res.code = 2;cout << "除零错误" << endl;break;}// 除零错误res.result = req.x % req.y;break;default:res.code = 3; // 操作符非法cout << "请输入合法的操作符 " << endl;break;}cout << "request: " << req.x << req.op << req.y << endl;// 做出回应write(newfd, &res, sizeof(res));}close(newfd);
}int main(int args, char *argv[])
{// 1.创建套接字文件int listenfd = Socket::Sock();// 2.绑定IP和端口号Socket::Bind(listenfd, atoi(argv[1]));// 3.进行监听Socket::Listen(listenfd);for (;;){// 4.获取连接int fd = Socket::Accept(listenfd);int *newfd = new int(fd);pthread_t tid;pthread_create(&tid, nullptr, handle, (void *)newfd);}return 0;
}

sever服务器端获取client端发送的request请求,并向client客户端做出response回应,将最终的计算结果返回。 

 运行结果如下。

运行结果符合预期。

情景二:制造一个网络计算器小程序(使用序列化和反序列化)。

我们使用json第三方库进行序列化和反序列化。

Protocol.hpp

#pragma once
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
using namespace std;// 客户端发送请求
typedef struct request
{int x;int y;char op;} request_t;// 服务端做出响应(回应)
typedef struct response
{int code;   // code表示sever端计算的结果是否正确int result; // 表示计算的结果
} response_t;//  request_t -> string
string SerializeRequest(const request_t &req)
{// 序列化Json::Value root; // 可以承装任何对象, json是一种kv式的序列化方案root["datax"] = req.x;root["datay"] = req.y;root["operator"] = req.op;Json::FastWriter writer;string json_string = writer.write(root);return json_string;
}// string -> request_t
void DeserializeRequest(const string &json_string, request_t &out)
{// 反序列化Json::Reader reader;Json::Value root;reader.parse(json_string, root);out.x = root["datax"].asInt();out.y = root["datay"].asInt();out.op = (char)root["operator"].asInt();
}string SerializeResponse(const response_t &resp)
{//序列化Json::Value root;root["code"] = resp.code;root["result"] = resp.result;Json::FastWriter writer;string res = writer.write(root);return res;
}void DeserializeResponse(const string &json_string, response_t &out)
{// 反序列化Json::Reader reader;Json::Value root;reader.parse(json_string, root);out.code = root["code"].asInt();out.result = root["result"].asInt();
}

序列化中有Value类对象和Writer类对象,Value类对象是一个万能对象是一个kv结构,可以接收任何对象。Writer类对象进行结构化数据转为字符串。

反序列化中有Value类对象和Reader类对象,Value对象和序列化一样,Reader类对象实现字符串转为结构化对象。

CalClient.cc

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Protocol.hpp"
#include "Sock.hpp"
#include <unistd.h>
#include <string.h>using namespace std;int main(int args, char *argv[])
{// 1.创建套接字文件int fd = Socket::Sock();// 2.发送链接Socket::Connect(fd, argv[1], atoi(argv[2]));// 3.发送请求request_t req;memset(&req, 0, sizeof(req));cout << "请输入x: ";cin >> req.x;cout << "请输入y: ";cin >> req.y;cout << "请输入op: ";cin >> req.op;// 对req进行序列化,然后写入string json_string = SerializeRequest(req);write(fd, json_string.c_str(), json_string.size());// 写都是输入型参数,读都是输出型参数// 4.获取回应// 对获取到的回应进行反序列化char buffer[1024];size_t s = read(fd, buffer, sizeof(buffer) - 1);if (s > 0){buffer[s] = 0;response_t res;memset(&res, 0, sizeof(res));string json_string1 = buffer;DeserializeResponse(json_string1, res);cout << "获取到了回应: " << res.code << " " << res.result << endl;}return 0;
}

client端先对发送的结构化请求序列化成字符串,然后进行序列化字符串的发送,最终对从sever端收到的序列化字符串进行反序列化成结构化响应。 

CalSever.cc

#include <iostream>
#include "Sock.hpp"
#include "Protocol.hpp"
#include <pthread.h>
#include <unistd.h>
#include <string.h>
using namespace std;// 5.提供服务
void *handle(void *args)
{int newfd = *(int *)args;delete (int *)args;// 获取请求,对接受到的request请求进行反序列化char buffer[1024];request_t req;memset(&req, 0, sizeof(req));size_t s = read(newfd, buffer, sizeof(buffer) - 1);if (s > 0){buffer[s] = 0;string jsonstring = buffer;DeserializeRequest(jsonstring, req);// 处理请求response_t res = {0, 0};switch (req.op){case '+':res.result = req.x + req.y;break;case '-':res.result = req.x - req.y;break;case '*':res.result = req.x * req.y;break;case '/':if (req.y == 0){res.code = 1;cout << "除零错误" << endl;break;} // 除零错误res.result = req.x / req.y;break;case '%':if (req.y == 0){res.code = 2;cout << "除零错误" << endl;break;}// 除零错误res.result = req.x % req.y;break;default:res.code = 3; // 操作符非法cout << "请输入合法的操作符 " << endl;break;}cout << "request: " << req.x << req.op << req.y << endl;// 做出回应// 对回应进行序列化string json_string1 = SerializeResponse(res);write(newfd, json_string1.c_str(), json_string1.size());}close(newfd);
}int main(int args, char *argv[])
{// 1.创建套接字文件int listenfd = Socket::Sock();// 2.绑定IP和端口号Socket::Bind(listenfd, atoi(argv[1]));// 3.进行监听Socket::Listen(listenfd);for (;;){// 4.获取连接int fd = Socket::Accept(listenfd);int *newfd = new int(fd);pthread_t tid;pthread_create(&tid, nullptr, handle, (void *)newfd);}return 0;
}

sever端先对从client端收到的序列化字符串数据进行反序列化成结构化请求, 然后最终将结构化响应序列化成字符串发送给client端。

运行结果如下。

运行结果符合预期。 

以上便是本期的序列化和反序列化所有内容,需要注意的是,不论是我们今天写的简单计算器小程序,和前两期写的基于TCP和UDP的网络小程序都其实是处于应用层的。

本期内容到此结束^_^ 

相关文章:

Linux网络之序列化和反序列化

目录 序列化和反序列化 上期我们学习了基于TCP的socket套接字编程接口&#xff0c;并实现了一个TCP网络小程序&#xff0c;本期我们将在此基础上进一步延伸学习&#xff0c;实现一个网络版简单计算器。 序列化和反序列化 在生活中肯定有这样一个情景。 上图大家肯定不陌生&a…...

linux设置mysql远程连接

首先保证服务器开放了mysql的端口 然后输入 mysql -u root -p 输入密码后即可进入mysql 然后再 use mysql; select user,host from user; update user set host"%" where user"root"; flush privileges; 再执行 select user,host from user; 即可看到变…...

react-native网络调试工具Reactotron保姆级教程

在React Native开发过程中&#xff0c;调试和性能优化是至关重要的环节。今天&#xff0c;就来给大家分享一个非常强大的工具——Reactotron&#xff0c;它就像是一个贴心的助手&#xff0c;能帮助我们更轻松地追踪问题、优化性能。下面就是一份保姆级教程哦&#xff01; 一、…...

erase() 【删数函数】的使用

**2025 - 01 - 25 - 第 48 篇 【函数的使用】 作者(Author) 文章目录 earse() - 删除函数一. vector中的 erase1 移除单个元素2 移除一段元素 二. map 中的erase1 通过键移除元素2 通过迭代器移除元素 earse() - 删除函数 一. vector中的 erase vector 是一个动态数组&#x…...

性能测试丨内存火焰图 Flame Graphs

内存火焰图的基本原理 内存火焰图是通过分析堆栈跟踪数据生成的一种图形化表现&#xff0c;能够展示应用程序在运行时各个函数的内存占用情况。火焰图的宽度代表了函数占用的内存量&#xff0c;而火焰的高度则显示了函数在调用栈中的层级关系。通过这种可视化方式&#xff0c;…...

AIGC的企业级解决方案架构及成本效益分析

AIGC的企业级解决方案架构及成本效益分析 一,企业级解决方案架构 AIGC(人工智能生成内容)的企业级解决方案架构是一个多层次、多维度的复杂系统,旨在帮助企业实现智能化转型和业务创新。以下是总结的企业级AIGC解决方案架构的主要组成部分: 1. 技术架构 企业级AIGC解决方…...

Linux 入门 常用指令 详细版

欢迎来到指令小仓库&#xff01;&#xff01; 宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 什么是指令&#xff1f; 指令和可执行程序都是可以被执行的-->指令就是可执行程序。 指令一定是在系统的每一个位置存在的。 1.ls指令 语法&#xff1a; ls [选项][目…...

【R语言】流程控制

R语言中&#xff0c;常用的流程控制函数有&#xff1a;repeat、while、for、if…else、switch。 1、repeat循环 repeat函数经常与 break 语句或 next 语句一起使用。 repeat ({x <- sample(c(1:7),1)message("x ", x, ",你好吗&#xff1f;")if (x …...

猿人学第一题 js混淆源码乱码

首先检查刷新网络可知&#xff0c;m参数被加密&#xff0c;这是一个ajax请求 那么我们直接去定位该路径 定位成功 观察堆栈之后可以分析出来这应该是一个混淆&#xff0c;我们放到解码平台去还原一下 window["url"] "/api/match/1";request function…...

计算机组成原理(2)王道学习笔记

数据的表示和运算 提问&#xff1a;1.数据如何在计算机中表示&#xff1f; 2.运算器如何实现数据的算术、逻辑运算&#xff1f; 十进制计数法 古印度人发明了阿拉伯数字&#xff1a;0&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;6&#…...

【AI日记】25.01.26

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】 AI kaggle 比赛&#xff1a;Forecasting Sticker Sales 读书 书名&#xff1a;自由宪章 律己 AI&#xff1a;6 小时作息&#xff1a;00:30-8:30短视频&#xff1a;大于 1 小时读书和写作&a…...

三. Redis 基本指令(Redis 快速入门-03)

三. Redis 基本指令(Redis 快速入门-03) 文章目录 三. Redis 基本指令(Redis 快速入门-03)1. Redis 基础操作&#xff1a;2. 对 key(键)操作&#xff1a;3. 对 DB(数据库)操作4. 最后&#xff1a; Reids 指定大全(指令文档)&#xff1a; https://www.redis.net.cn/order/ Redis…...

设计模式的艺术-代理模式

结构性模式的名称、定义、学习难度和使用频率如下表所示&#xff1a; 1.如何理解代理模式 代理模式&#xff08;Proxy Pattern&#xff09;&#xff1a;给某一个对象提供一个代理&#xff0c;并由代理对象控制对原对象的引用。代理模式是一种对象结构型模式。 代理模式类型较多…...

C#新语法

目录 顶级语句&#xff08;C#9.0&#xff09; using 全局using指令&#xff08;C#10.0&#xff09; using资源管理问题 using声明&#xff08;C#8.0&#xff09; using声明陷阱 错误写法 正确写法 文件范围的命名空间声明&#xff08;C#10.0&#xff09; 可空引用类型…...

微信小程序压缩图片

由于wx.compressImage(Object object) iOS 仅支持压缩 JPG 格式图片。所以我们需要做一下特殊的处理&#xff1a; 1.获取文件&#xff0c;判断文件是否大于设定的大小 2.如果大于则使用canvas进行绘制&#xff0c;并生成新的图片路径 3.上传图片 async chooseImage() {let …...

通义灵码插件保姆级教学-IDEA(安装及使用)

一、JetBrains IDEA 中安装指南 官方下载指南&#xff1a;通义灵码安装教程-阿里云 步骤 1&#xff1a;准备工作 操作系统&#xff1a;Windows 7 及以上、macOS、Linux&#xff1b; 下载并安装兼容的 JetBrains IDEs 2020.3 及以上版本&#xff0c;通义灵码与以下 IDE 兼容&…...

windows下本地部署安装hadoop+scala+spark-【不需要虚拟机】

注意版本依赖【本实验版本如下】 Hadoop 3.1.1 spark 2.3.2 scala 2.11 1.依赖环境 1.1 java 安装java并配置环境变量【如果未安装搜索其他教程】 环境验证如下&#xff1a; C:\Users\wangning>java -version java version "1.8.0_261" Java(TM) SE Runti…...

倍频增量式编码器--角度插值法输出A,B(Aangular Interpolation)

问题是&#xff1a; 最大速度&#xff0c;周期刻度&#xff0c;最小细分刻度&#xff0c;可以计算得到&#xff1a; 结论&#xff1a; 按照最高速度采样&#xff1b;数字A,B输出间隔时间&#xff1a;按照计算角度 插入细分角度运算算时间&#xff08;最快速度&#xff09;&a…...

LSM对于特殊数据的优化手段

好的&#xff0c;我现在需要帮助用户理解如何针对不同的特殊工作负载优化LSM树结构。用户提到了四种情况&#xff1a;时态数据、小数据、半排序数据和追加为主的数据。我需要分别解释每种情况下的优化方法&#xff0c;并参考用户提供的LHAM的例子&#xff0c;可能还有其他例子。…...

83,【7】BUUCTF WEB [MRCTF2020]你传你[特殊字符]呢

进入靶场 图片上这个人和另一道题上的人长得好像 54&#xff0c;【4】BUUCTF WEB GYCTF2020Ezsqli-CSDN博客 让我们上传文件 桌面有啥传啥 /var/www/html/upload/344434f245b7ac3a4fae0a6342d1f94a/123.php.jpg 成功后我就去用蚁剑连了&#xff0c;连不上 看了别的wp知需要…...

Go语言入门指南(二): 数据类型

文章创作不易&#xff0c;麻烦大家点赞关注转发一键三连。 在上一篇文章&#xff0c;我们已经完成了开发环境的搭建&#xff0c;成功创建了第一个“Hello, World”程序&#xff0c;并且对变量的声明和初始化有了初步的认识。在这篇文章中&#xff0c;我们将主要介绍Go语言的数据…...

2025.1.26机器学习笔记:C-RNN-GAN文献阅读

2025.1.26周报 文献阅读题目信息摘要Abstract创新点网络架构实验结论缺点以及后续展望 总结 文献阅读 题目信息 题目&#xff1a; C-RNN-GAN: Continuous recurrent neural networks with adversarial training会议期刊&#xff1a; NIPS作者&#xff1a; Olof Mogren发表时间…...

FAST-DDS and ROS2 RQT connect

reference: FAST-DDS与ROS2通信_ros2 收fastdds的数据-CSDN博客 software version: repositories: foonathan_memory_vendor: type: git url: https://github.com/eProsima/foonathan_memory_vendor.git version: v1.1.0 fastcdr: …...

GESP2024年3月认证C++六级( 第三部分编程题(2)好斗的牛)

参考程序&#xff08;暴力枚举&#xff09; #include <iostream> #include <vector> #include <algorithm> using namespace std; int N; vector<int> a, b; int ans 1e9; int main() {cin >> N;a.resize(N);b.resize(N);for (int i 0; i &l…...

记一次STM32编译生成BIN文件过大的问题(基于STM32CubeIDE)

文章目录 问题描述解决方法更多拓展 问题描述 最近在一个项目中使用了 STM32H743 单片机&#xff08;基于 STM32CubeIDE GCC 开发&#xff09;&#xff0c;它的内存分为了 DTCMRAM RAM_D1 RAM_D2 …等很多部分。其中 DTCM 的速度是比通常的内存要快的&#xff0c;缺点是不支持…...

【暴力洗盘】的实战技术解读-北玻股份和三变科技

龙头的上攻与回调动作都是十分惊人的。不惊人不足以吸引投资者的关注&#xff0c;不惊人也就不能成为龙头了。 1.建筑节能概念--北玻股份 建筑节能&#xff0c;是指在建筑材料生产、房屋建筑和构筑物施工及使用过程中&#xff0c;满足同等需要或达到相同目的的条件下&#xf…...

Day42:列表的组合

在Python 中&#xff0c;列表的组合是指将两个或多个列表合并成一个新的列表。Python 提供了多种方法来实现这一操作&#xff0c;每种方法都有其特定的应用场景。今天我们将学习如何通过不同的方式组合列表。 1. 使用 运算符进行列表合并 最直接的方式是使用 运算符&#x…...

mantisbt添加修改用户密码

文章目录 问题当前版本安装流程创建用户修改密码老的方式探索阶段 问题 不太好改密码啊。貌似必须要域名要发邮件。公司太穷&#xff0c;看不见的东西不关心&#xff0c;只能改源码了。 当前版本 当前mantisbt版本 2.27 php版本 7.4.3 安装流程 &#xff08;下面流程不是…...

DroneXtract:一款针对无人机的网络安全数字取证工具

关于DroneXtract DroneXtract是一款使用 Golang 开发的适用于DJI无人机的综合数字取证套件&#xff0c;该工具可用于分析无人机传感器值和遥测数据、可视化无人机飞行地图、审计威胁活动以及提取多种文件格式中的相关数据。 功能介绍 DroneXtract 具有四个用于无人机取证和审…...

简单树形菜单

引言 在网页开发中&#xff0c;树形菜单是一种非常实用的&#xff0c;它可以清晰地展示具有层级关系的数据&#xff0c;并且能够方便用户进行导航和操作。 整体思路 整个项目主要分为三个部分&#xff1a;HTML 结构搭建、CSS 样式设计和 JavaScript 交互逻辑实现。通过 XMLHt…...