【Linux】应用层自定义协议与序列化

🌈 个人主页:Zfox_
🔥 系列专栏:Linux

目录

  • 一:🔥 应用层
    • 🦋 再谈 "协议"
    • 🦋 网络版计算器
    • 🦋 序列化 和 反序列化
  • 二:🔥 重新理解 read、 write、 recv、 send 和 tcp 为什么支持全双工
  • 三:🔥 开始实现
    • 🦋 定制协议
  • 四:🔥 为关于流式数据的处理
  • 五:🔥 Jsoncpp
    • 🦋 特性
    • 🦋 安装
    • 🦋 序列化
    • 🦋 反序列化
    • 🦋 总结
  • 六:🔥 共勉

一:🔥 应用层

📚 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层

🦋 再谈 “协议”

协议是一种 “约定”. socket api 的接口, 在读写数据时, 都是按 “字符串” 的方式来发送接收的. 如果我们要传输一些 “结构化的数据” 怎么办呢?

其实, 协议就是双方约定好的结构化的数据

🦋 网络版计算器

  • 例如, 我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端

📚 约定方案一:

  • 客户端发送一个形如"1+1"的字符串;
  • 这个字符串中有两个操作数, 都是整形;
  • 两个数字之间会有一个字符是运算符, 运算符只能是 + ;
  • 数字和运算符之间没有空格;

📚 约定方案二:

  • 定义结构体 来表示我们需要交互的信息;
  • 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体;
  • 这个过程叫做 "序列化""反序列化"

🦋 序列化 和 反序列化

在这里插入图片描述
无论我们采用方案一, 还是方案二, 还是其他的方案, 只要保证, 一端发送时构造的数据, 在另一端能够正确的进行解析, 就是 ok 的. 这种约定, 就是 应用层协议

但是, 为了让我们深刻理解协议, 我们打算自定义实现一下协议的过程。

  • 我们采用方案 2, 我们也要体现协议定制的细节
  • 我们要引入 序列化反序列化, 只不过我们直接采用现成的方案 – jsoncpp库
  • 我们要对 socket 进行字节流的读取处理

二:🔥 重新理解 read、 write、 recv、 send 和 tcp 为什么支持全双工

在这里插入图片描述
所以:

  • 在任何一台主机上, TCP 连接既有发送缓冲区, 又有接受缓冲区, 所以, 在内核中, 可以在发消息的同时, 也可以收消息, 即全双工
  • 这就是为什么一个 tcp sockfd 读写都是它的原因
  • 实际数据什么时候发, 发多少, 出错了怎么办, 由 TCP 控制, 所以 TCP 叫做传输控制协议
  • 那么,也就需要应用层保证自己报文的完整性

三:🔥 开始实现

🦋 定制协议

📚 基本结构

  • 定制基本的结构化字段, 这个就是协议

报文的自描述字段:
“len\r\nx op y\r\n” : \r\n 不属于报文的一部分, 约定
在这里插入图片描述

#pragma once#include <string>   
#include <iostream>
#include <sstream>
#include <memory>
#include <jsoncpp/json/json.h>// _x _oper _y
class Request 
{
public:Request() : _x(0), _y(0), _oper(0) {}Request(int x, int y, char oper) : _x(x), _y(y), _oper(oper) {}bool Serialize(std::string &out_string){Json::Value root;root["x"] = _x;root["y"] = _y;root["oper"] = _oper;Json::StreamWriterBuilder wb;std::unique_ptr<Json::StreamWriter> w(wb.newStreamWriter());std::stringstream ss;w->write(root, &ss);out_string = ss.str();return true;}bool Deserialize(std::string in_string) {Json::Value root;Json::Reader reader;bool parsingSuccessful = reader.parse(in_string, root);if(!parsingSuccessful){std::cout << "Failed to parse JSON: " << reader.getFormattedErrorMessages() << std::endl;return false;}_x = root["x"].asInt();_y = root["y"].asInt();_oper = root["oper"].asInt();return true;}void Print(){std::cout << _x << std::endl;std::cout << _oper << std::endl;std::cout << _y << std::endl;}int X() const {return _x;}int Y() const {return _y;}char Oper() const {return _oper;}private:int _x;int _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_string){Json::Value root;root["result"] = _result;root["code"] = _code;Json::StreamWriterBuilder wb;std::unique_ptr<Json::StreamWriter> w(wb.newStreamWriter());std::stringstream ss;w->write(root, &ss);out_string = ss.str();return true;}bool Deserialize(std::string in_string){Json::Value root;Json::Reader reader;bool parsingSuccessful = reader.parse(in_string, root);if(!parsingSuccessful){std::cout << "Failed to parse JSON: " << reader.getFormattedErrorMessages() << std::endl;return false;}_result = root["result"].asInt();_code = root["code"].asInt();return true;}int Result() const {return _result;}int Code() const {return _code;}void SetResult(int result) {_result = result;}void SetCode(int code) {_code = code;}private:int _result;  // 结果int _code;    // 出错码: 0, 1, 2, 3, 4
};

四:🔥 为关于流式数据的处理

  • 你如何保证你每次读取就能读完请求缓冲区的所有内容?
  • 你怎么保证读取完毕或者读取没有完毕的时候, 读到的就是一个完整的请求呢?
  • 处理 TCP 缓冲区中的数据, 一定要保证正确处理请求
const std::string Sep = "\r\n";// 报头
bool Encode(std::string &message)
{if(message.empty()) return false;std::string package = std::to_string(message.size()) + Sep + message + Sep;message = package;return true;
}// len\n{json}\n  解包
// 123\r\n{json}\r\n -> {json}
bool Decode(std::string &package, std::string *content)
{auto pos = package.find(Sep);if(pos == std::string::npos) return false; std::string content_length_str = package.substr(0, pos); int content_length = std::stoi(content_length_str);int full_length = content_length_str.size() + content_length + 2 * Sep.size(); if(package.size() < full_length) return false;*content = package.substr(pos + Sep.size(), content_length);package.erase(0, full_length);return true;
}

📚 完整的处理过程应该是:
在这里插入图片描述

五:🔥 Jsoncpp

Jsoncpp 是一个用于处理 JSON 数据的 C++ 库。 它提供了将 JSON 数据序列化为字符串以及从字符串反序列化为 C++ 数据结构的功能。 Jsoncpp 是开源的, 广泛用于各种需要处理 JSON 数据的 C++ 项目中。

🦋 特性

  1. 简单易用: Jsoncpp 提供了直观的 API, 使得处理 JSON 数据变得简单。
  2. 高性能: Jsoncpp 的性能经过优化, 能够高效地处理大量 JSON 数据。
  3. 全面支持: 支持 JSON 标准中的所有数据类型, 包括对象、 数组、 字符串、 数字、 布尔值和 null。
  4. 错误处理: 在解析 JSON 数据时, Jsoncpp 提供了详细的错误信息和位置, 方便开发者调试。

当使用 Jsoncpp 库进行 JSON 的序列化和反序列化时, 确实存在不同的做法和工具类可供选择。 以下是对 Jsoncpp 中序列化和反序列化操作的详细介绍:

🦋 安装

C++
ubuntu: sudo apt-get install libjsoncpp-dev
Centos: sudo yum install jsoncpp-devel

🦋 序列化

📚 序列化指的是将数据结构或对象转换为一种可以存储或传输的格式(如字节流、JSON、XML 等)
Jsoncpp 提供了多种方式进行序列化:

  1. 使用 Json::StreamWriter
    ○ 优点: 提供了更多的定制选项, 如缩进、 换行符等。
    ○ 示例:
C++
#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include <jsoncpp/json/json.h>int main()
{Json::Value root;root["name"] = "joe";root["sex"] = "男";Json::StreamWriterBuilder wbuilder; // StreamWriter 的工厂std::unique_ptr<Json::StreamWriter> writer(wbuilder.newStreamWriter());std::stringstream ss;writer->write(root, &ss);std::cout << ss.str() << std::endl;return 0;
}$ ./test.exe
{"name" : "joe","sex" : "男"
}
  1. 使用 Json::Value 的 toStyledString 方法:
    ○ 优点: 将 Json::Value 对象直接转换为格式化的 JSON 字符串。
    ○ 示例:
C++
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>int main()
{Json::Value root;root["name"] = "joe";root["sex"] = "男";std::string s = root.toStyledString();std::cout << s << std::endl;return 0;
} $ ./test.exe
{"name" : "joe","sex" : "男"
}
  1. 使用 Json::FastWriter
    ○ 优点: 比 StyledWriter 更快, 因为它不添加额外的空格和换行符。
    ○ 示例:
C++
#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include <jsoncpp/json/json.h>int main()
{Json::Value root;root["name"] = "joe";root["sex"] = "男";Json::FastWriter writer;std::string s = writer.write(root);std::cout << s << std::endl;return 0;
} $ ./test.exe
{"name":"joe","sex":"男"}#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include <jsoncpp/json/json.h>int main()
{Json::Value root;root["name"] = "joe";root["sex"] = "男";// Json::FastWriter writer;Json::StyledWriter writer;std::string s = writer.write(root);std::cout << s << std::endl;return 0;
} $ ./test.exe
{"name" : "joe","sex" : "男"
}

🦋 反序列化

反序列化指的是将序列化后的数据重新转换为原来的数据结构或对象。 Jsoncpp 提供了以下方法进行反序列化:

  1. 使用 Json::Reader:
    ○ 优点: 提供详细的错误信息和位置, 方便调试。
    ○ 示例:
C++
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>int main() {// JSON 字符串std::string json_string = "{\"name\":\"张三\",\"age\":30, \"city\":\"北京\"}";// 解析 JSON 字符串Json::Reader reader;Json::Value root;// 从字符串中读取 JSON 数据bool parsingSuccessful = reader.parse(json_string, root);if (!parsingSuccessful) {// 解析失败, 输出错误信息std::cout << "Failed to parse JSON: " << reader.getFormattedErrorMessages() << std::endl;return 1;
} // 访问 JSON 数据std::string name = root["name"].asString();int age = root["age"].asInt();std::string city = root["city"].asString();// 输出结果std::cout << "Name: " << name << std::endl;std::cout << "Age: " << age << std::endl;std::cout << "City: " << city << std::endl;return 0;
} $ ./test.exe
Name: 张三
Age: 30
City: 北京
  1. 使用 Json::CharReader 的派生类(不推荐了, 上面的足够了):
    ○ 在某些情况下, 你可能需要更精细地控制解析过程, 可以直接使用 Json::CharReader 的派生类。
    ○ 但通常情况下, 使用 Json::parseFromStream 或 Json::Reader 的 parse 方法就足够了。

🦋 总结

toStyledString、 StreamWriter 和 FastWriter 提供了不同的序列化选项,你可以根据具体需求选择使用。
Json::Reader 和 parseFromStream 函数是 Jsoncpp 中主要的反序列化工具,它们提供了强大的错误处理机制。
在进行序列化和反序列化时, 请确保处理所有可能的错误情况, 并验证输入和输出的有效性。

六:🔥 共勉

以上就是我对 【Linux】应用层自定义协议与序列化 的理解,觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉
在这里插入图片描述

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

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

相关文章

数字化贷款管理:助贷系统软件为贷款中介带来的五大改变

随着金融行业的数字化转型&#xff0c;贷款中介的业务模式也在不断创新。助贷系统作为数字化管理的核心工具&#xff0c;正在为贷款中介带来深刻的变革。本文将从五个方面探讨助贷系统软件如何改变贷款中介行业的管理模式&#xff0c;提升业务效率&#xff0c;降低运营成本。乐…

MIAOYUN信创云原生项目亮相西部“中试”生态对接活动

近日&#xff0c;以“构建‘中试’生态&#xff0c;赋能科技成果转化”为主题的“科创天府智汇蓉城”西部“中试”生态对接活动在成都高新区菁蓉汇隆重开幕。活动分为成果展览、“中试”生态主场以及成果路演洽谈对接三大板块。在成果展览环节&#xff0c;成都元来云志科技有限…

一文简要了解为什么需要RAG、核心原理与应用场景

欢迎来到AI应用探索&#xff0c;这里专注于探索AI应用。 一、为什么需要RAG&#xff0c;它解决了哪些问题 在自然语言处理领域&#xff0c;生成式预训练模型&#xff08;如GPT&#xff09;已经展示了强大的文本生成能力。然而&#xff0c;这些模型有以下局限性&#xff1a; 知…

gametime

gametime 一、查壳 无壳&#xff0c;32位 二、IDA分析 先看看main 妈呀&#xff0c;好多函数&#xff0c;脑子有点乱 先运行下EXE看看有什么突破口没 可以看出是游戏&#xff0c;明显是看你的输入对不对&#xff0c;来通关的&#xff0c;所以有关判定的条件或者函数是解题…

基于机器学习随机森林算法的个人职业预测研究

1.背景调研 随着信息技术的飞速发展&#xff0c;特别是大数据和云计算技术的广泛应用&#xff0c;各行各业都积累了大量的数据。这些数据中蕴含着丰富的信息和模式&#xff0c;为利用机器学习进行职业预测提供了可能。机器学习算法的不断进步&#xff0c;如深度学习、强化学习等…

【Linux】Socket编程-TCP构建自己的C++服务器

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; Socket 编程 TCP &#x1f98b; TCP socket API 详解&#x1f98b; 多线程远程命令执行&#x1f98b; 网络版计算器&#xff08;应用层自定义协议与序列化…

C++(二十一)

前言&#xff1a; 本文承接上文&#xff0c;将详细讲解指针概念。 一&#xff0c;通过指针了解变量的数值。 在将变量地址存入指针后&#xff0c;从指针反推也可以知道原变量的值&#xff0c;若想进行反退&#xff0c;就需要使用间接引用运算符&#xff1a;*。 语法&#x…

Windows图形界面(GUI)-QT-C/C++ - Qt List Widget详解与应用

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​链接点击跳转博客主页 目录 QListWidget概述 使用场景 常见样式 QListWidget属性设置 显示方式 (Display) 交互行为 (Interaction) 高级功能 (Advanced) QListWidget常见操作 内容处理 增加项目 删除项目…

一文了解如何使用 DBeaver 管理 DolphinDB

在日常的数据开发、分析和数据库运维中&#xff0c;一款优秀的 IDE 能够极大地提升工作效率。DBEaver 是一款由 Java 编写的一站式跨平台连接器&#xff0c;其社区版本已能支持连接近百种数据库&#xff0c;受到广大开发者的喜爱。近期。DolphinDB 与 DBeaver 团队共同努力&…

CSRF攻击XSS攻击

概述 ​在 HTML 中&#xff0c;<a>, <form>, <img>, <script>, <iframe>, <link> 等标签以及 Ajax 都可以指向一个资源地址&#xff0c;而所谓的跨域请求就是指&#xff1a;当前发起请求的域与该请求指向的资源所在的域不一样。这里的域指…

【机器学习实战入门】使用 Pandas 和 OpenCV 进行颜色检测

Python 颜色检测项目 今天的项目将非常有趣和令人兴奋。我们将与颜色打交道&#xff0c;并在项目过程中学习许多概念。颜色检测对于识别物体来说是必要的&#xff0c;它也被用作各种图像编辑和绘图应用的工具。 什么是颜色检测&#xff1f; 颜色检测是检测任何颜色名称的过程…

vue2 web 多标签输入框 elinput是否当前焦点

又来分享一点点工作积累及解决方案 产品中需要用户输入一些文字后按下回车键生成标签来显示在页面上&#xff0c;经过尝试与改造完成如下&#xff1a; <template><div class"tags-view" click"beginInput"><el-tag :key"index" …

SSE 实践:用 Vue 和 Spring Boot 实现实时数据传输

前言 大家好&#xff0c;我是雪荷。最近我在灵犀 BI 项目中引入了 SSE 技术&#xff0c;以保证图表的实时渲染&#xff0c;当图表渲染完毕服务端推送消息至浏览器端触发重新渲染。 什么是 SSE&#xff1f; SSE 全称为 Server-Send Events 意思是服务端推送事件。 SSE 相比于 …

记录一次 centos 启动失败

文章目录 现场1分析1现场2分析2搜索实际解决过程 现场1 一次断电,导致 之前能正常启动的centos 7.7 起不来了有部分log , 关键信息如下 [1.332724] XFS(sda3): Internal error xfs ... at line xxx of fs/xfs/xfs_trans.c [1.332724] XFS(sda3): Corruption of in-memory data…

【狂热算法篇】探秘图论之 Floyd 算法:解锁最短路径的神秘密码(通俗易懂版)

&#xff1a; 羑悻的小杀马特.-CSDN博客羑悻的小杀马特.擅长C/C题海汇总,AI学习,c的不归之路,等方面的知识,羑悻的小杀马特.关注算法,c,c语言,青少年编程领域.https://blog.csdn.net/2401_82648291?spm1010.2135.3001.5343 在本篇文章中&#xff0c;博主将带大家去学习所谓的…

【k8s面试题2025】3、练气中期

体内灵气的量和纯度在逐渐增加。 文章目录 在 Kubernetes 中自定义 Service端口报错常用控制器Kubernetes 中拉伸收缩副本失效设置节点容忍异常时间Deployment 控制器的升级和回滚日志收集资源监控监控 Docker将 Master 节点设置为可调度 在 Kubernetes 中自定义 Service端口报…

Ubuntu20.04取消root账号自动登录的方法,触觉智能RK3568开发板演示

Ubuntu20.04默认情况下为root账号自动登录&#xff0c;本文介绍如何取消root账号自动登录&#xff0c;改为通过输入账号密码登录&#xff0c;使用触觉智能EVB3568鸿蒙开发板演示&#xff0c;搭载瑞芯微RK3568&#xff0c;四核A55处理器&#xff0c;主频2.0Ghz&#xff0c;1T算力…

LeetCode | 解锁数组与字符串的秘密:经典题型详解与高效解法

1.理论 1. 1.核心概念 1.1.1.数组(Array) 定义&#xff1a;存储相同数据类型的元素的线性集合。 特点&#xff1a;支持随机访问&#xff08;通过索引&#xff09;;元素存储在连续内存中&#xff0c;支持高效的读写操作。 时间复杂度&#xff1a;访问&#xff1a;O(1);插入…

怎么修复损坏的U盘?而且不用格式化的方式!

当你插入U盘时&#xff0c;若电脑弹出“需要格式化才能使用”提示&#xff0c;且无法打开或读取其中的数据&#xff0c;说明U盘极有可能已经损坏。除此之外&#xff0c;若电脑在连接U盘后显示以下信息&#xff0c;也可能意味着U盘出现问题&#xff0c;需要修复损坏的U盘&#x…

数据结构漫游记:动态实现栈(stack)

嘿&#xff0c;各位技术潮人&#xff01;好久不见甚是想念。生活就像一场奇妙冒险&#xff0c;而编程就是那把超酷的万能钥匙。此刻&#xff0c;阳光洒在键盘上&#xff0c;灵感在指尖跳跃&#xff0c;让我们抛开一切束缚&#xff0c;给平淡日子加点料&#xff0c;注入满满的pa…