计算机网络基础(3)_应用层自定义协议与序列化

news2024/11/14 19:44:19

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

计算机网络基础(3)_应用层自定义协议与序列化

收录于专栏【计算机网络】
本专栏旨在分享学习计算机网络的一点学习笔记,欢迎大家在评论区交流讨论💌 

目录

1. 应用层 

再谈协议 :

网络版计算器 :  

2. 序列化和反序列化

3. 重新理解 read, write, recv, send 和 tcp 为什么支持全双工

4. 流式数据处理

5. Jsoncpp

特性 :

安装

6. Json实现序列化和反序列化

序列化 : 

反序列化 :

总结 : 

7. Json::Value  

构造函数 : 

访问元素

类型检查

赋值和类型转换

数组和对象操作 


1. 应用层 

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

再谈协议 :

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

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

网络版计算器 :  

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

约定方案一 : 

客户端发送一个形如 "1 + 1" 的字符串

这个字符串中有两个操作数, 都是整形

两个数字之间会有一个字符是运算符, 运算符只能是 "+"

数字和运算符之间没有空格

.......

定义方案二 :  

定义结构体来表示我们需要交互的信息 

发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转换回结构体

这个过程叫做 "序列化" 和 "反序列化"

2. 序列化和反序列化

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

如果我们采用方案二, 我们也要体现协议定制的细节

我们要引用序列化和反序列化, 只不过我们直接采用了现成的方案 --- jsoncpp 库

我们要对 socket 进行字节流的读取处理

3. 重新理解 read, write, recv, send 和 tcp 为什么支持全双工

所以 : 

1. 在任何一台主机上, TCP 连接既有发送缓冲区, 又有接收缓冲区, 所以, 在内核中, 可以在发消息的同时, 也可以收消息, 即全双工

2. 这就是为什么一个 tcp sockfd 读写都是它的原因

3. 实际数据什么时候发, 发多少, 出错了怎么办, 由 TCP 控制, 所以 TCP 叫做传输层控制协议

4. 流式数据处理

你如何保证你每次读取就能读完请求缓冲区的所有内容?

你怎么保证读取完毕或者读取没有完毕的时候, 读到的就是一个完整的请求呢?

处理 TCP 缓冲区中的数据, 一定要保证正确处理请求

const std::string ProtSep = " ";
const std::string LineBreakSep = "\n";
// "len\nx op y\n" : \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\nx op y\n" : \n 不属于报文的一部分,约定// 我无法保证 package 就是一个独立的完整的报文
// "l
// "len
// "len\n
// "len\nx
// "len\nx op
// "len\nx op y
// "len\nx op y\n"
// "len\nx op y\n""len
// "len\nx op y\n""len\n
// "len\nx op
// "len\nx op y\n""len\nx op y\n"
// "len\nresult code\n""len\nresult code\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;
}

 所以完整的处理过程为 :

5. Jsoncpp

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

特性 :

1. 简单易用 : Jsoncpp 提供了直观的 API, 使得处理 JSON 数据变得简单.

2. 高性能 : Jsoncpp 的性能经过优化, 能够高效地处理大量 JSON 数据.

3. 全面支持 : 支持 JSON 标准中的所有数据类型, 包括对象, 数组, 字符串, 数字, 布尔值和 null

4. 错误处理 : 在解析 JSON 数据时, Jsoncpp 提供了详细的错误信息和位置, 方便开发者调试

安装

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

6. Json实现序列化和反序列化

序列化 : 

序列化指的是将数据结构或对象转换为一种格式, 以便在网络上传输或存储到文件中. Jsoncpp 提供了多种方式进行序列化 : 

1. 使用 Json:Value 的 toStyledString 方法 :

优点 : 将 Json::Value 对象直接转换为格式化的 JSON 字符串

实例 : 

#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>

int main()
{
    Json::Value root;
    root["name"] = "joe";
    root["sex"] = "男";
    std::cout << root << std::endl;
    return 0;
}

2. 使用 Json::StreamWriter : (推荐)

优点 : 提供了更多的定制选项, 如缩进, 换行符等

示例 :

#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 swb;// StreamWriter 的工厂
    std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
    std::stringstream ss;
    sw->write(root, &ss);
    std::cout << ss.str() << std::endl;
    return 0;
}

3. 使用 Json::FastWrite : 

优点 : 比 StyleWriter 更快, 因为它不添加额外的空格和换行符

示例 :  

#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;
}

#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;
}

反序列化 :

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

1. 使用Json::Reader :

优点 : 提供详细的错误信息和位置, 方便调试.

示例 :

#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;
}

2. 使用 Json::CharReader 的派生类 (不推荐, 上面的足够了) 

在某些情况下, 你可能需要更精细地控制解析过程, 可以直接使用 Json::CharReader 的派生类.

但通常情况下, 使用 Json::parseFromStream 或 Json::Reader 的 parse 方法足够了. 

总结 : 

1. toStyledString, StreamWrite 和 FastWrite 提供了不同的序列化选项, 你可以根据具体需求选择使用.

2. Json::Reader 和parseFromStream 函数是 Jsoncpp 中主要的反序列化工具, 它们提供了强大的错误处理机制

3. 在进行序列化和反序列化时, 请确保处理所有可能的错误情况, 并验证输入和输出的有效性

7. Json::Value  

Json::Value 是 Jsoncpp 库中一个重要类, 用于表示和操作 JSON 数据结构, 以下是一些常用的 Json::Value 操作列表 : 

构造函数 : 

• Json::Value():默认构造函数,创建一个空的 Json::Value 对象。

Json::Value(ValueType type, bool allocated = false):根据给定的ValueType(如 nullValue, intValue, stringValue 等)创建一个Json::Value对象

访问元素

Json::Value& operator[](const char* key):通过键(字符串)访问对象中的元素。如果键不存在,则创建一个新的元素。

Json::Value& operator[](const std::string& key):同上,但使用std::string 类型的键。

Json::Value& operator[](ArrayIndex index):通过索引访问数组中的元素。如果索引超出范围,则创建一个新的元素。

Json::Value& at(const char* key):通过键访问对象中的元素,如果键不存在则抛出异常。

Json::Value& at(const std::string& key):同上,但使用std::string类型的键。 

类型检查

• bool isNull():检查值是否为 null。

• bool isBool():检查值是否为布尔类型。

• bool isInt():检查值是否为整数类型。

• bool isInt64():检查值是否为 64 位整数类型。

• bool isUInt():检查值是否为无符号整数类型。

• bool isUInt64():检查值是否为 64 位无符号整数类型。

• bool isIntegral():检查值是否为整数或可转换为整数的浮点数。

• bool isDouble():检查值是否为双精度浮点数。

• bool isNumeric():检查值是否为数字(整数或浮点数)。

• bool isString():检查值是否为字符串。

• bool isArray():检查值是否为数组。

• bool isObject():检查值是否为对象(即键值对的集合)。

赋值和类型转换

Json::Value& operator=(bool value):将布尔值赋给Json::Value 对象。

Json::Value& operator=(int value):将整数赋给 Json::Value 对象。

Json::Value& operator=(unsigned int value):将无符号整数赋给Json::Value 对象。

Json::Value& operator=(Int64 value):将 64 位整数赋给Json::Value对象。

Json::Value& operator=(UInt64 value):将 64 位无符号整数赋给Json::Value 对象。

Json::Value& operator=(double value):将双精度浮点数赋给Json::Value 对象。

Json::Value& operator=(const char* value):将C 字符串赋给Json::Value 对象。

Json::Value& operator=(const std::string& value):将std::string赋给 Json::Value 对象。

bool asBool():将值转换为布尔类型(如果可能)。

int asInt():将值转换为整数类型(如果可能)。

Int64 asInt64():将值转换为 64 位整数类型(如果可能)。

unsigned int asUInt():将值转换为无符号整数类型(如果可能)。

UInt64 asUInt64():将值转换为 64 位无符号整数类型(如果可能)。

double asDouble():将值转换为双精度浮点数类型(如果可能)。

std::string asString():将值转换为字符串类型(如果可能)

数组和对象操作 

size_t size():返回数组或对象中的元素数量。

bool empty():检查数组或对象是否为空。

void resize(ArrayIndex newSize):调整数组的大小。

void clear():删除数组或对象中的所有元素。

void append(const Json::Value& value):在数组末尾添加一个新元素。

Json::Value& operator[](const char* key, const Json::Value& defaultValue = Json::nullValue):在对象中插入或访问一个元素,如果键不存在则使用默认值。

Json::Value& operator[](const std::string& key, const Json::Value& defaultValue = Json::nullValue):同上,但使用std::string类型的

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

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

相关文章

E2E、CRC、Checksum、Rollingcounter

文章目录 前言1、E2E2、CRC3、Checksum4、Rollingcounter总结 前言 在专栏文章仿真CAN报文发送的CRC校验算法&#xff08;附CAPL代码&#xff09;和同星TSMaster中如何自定义E2E校验算法中分别给出了CRC算法和E2E校验实现&#xff0c;从中也明白了为什么在测试中需要去做这些仿…

嵌入式硬件杂谈(一)-推挽 开漏 高阻态 上拉电阻

引言&#xff1a;对于嵌入式硬件这个庞大的知识体系而言&#xff0c;太多离散的知识点很容易疏漏&#xff0c;因此对于这些容易忘记甚至不明白的知识点做成一个梳理&#xff0c;供大家参考以及学习&#xff0c;本文主要针对推挽、开漏、高阻态、上拉电阻这些知识点的学习。 目…

二叉树面试题(C 语言)

目录 1. 单值二叉树2. 相同的树3. 对称二叉树4. 二叉树的前序遍历5. 二叉树的中序遍历6. 二叉树的后序遍历7. 另一颗树的子树8. 通过前序遍历返回中序遍历 1. 单值二叉树 题目描述&#xff1a; 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。只有…

MFC中Excel的导入以及使用步骤

参考地址 在需要对EXCEL表进行操作的类中添加以下头文件&#xff1a;若出现大量错误将其放入stdafx.h中 #include "resource.h" // 主符号 #include "CWorkbook.h" //单个工作簿 #include "CRange.h" //区域类&#xff0c;对Excel大…

【C++】类中的“默认成员函数“--构造函数、析构函数、拷贝构造、赋值运算符重载

目录 "默认"成员函数 概念引入&#xff1a; 一、构造函数 问题引入&#xff1a; 1&#xff09;构造函数的概念 2&#xff09;构造函数实例 3&#xff09;构造函数的特性 4)关于默认生成的构造函数 (默认构造函数) 默认构造函数未完成初始化工作实例: 二…

LeetCode【0052】N皇后II

本文目录 1 中文题目2 求解方法&#xff1a;位运算回溯法2.1 方法思路2.2 Python代码2.3 复杂度分析 3 题目总结 1 中文题目 n 皇后问题 研究的是如何将 n 个皇后放置在 n n 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回 n 皇后问…

C语言-详细讲解-P1009 [NOIP1998 普及组] 高精度阶乘之和

目录 1.题目要求 2.题目解读 3.代码实现 4.一些小细节 1.数组储存大整数方式 2.memset函数介绍 3.digit与sum的关系 1.题目要求 2.题目解读 这道题本质就是高精度乘法高精度加法的结合&#xff0c;我之前有出过 高精度算法-保姆级讲解 希望详细了解的小伙伴可以去…

浅谈:基于三维场景的视频融合方法

视频融合技术的出现可以追溯到 1996 年 , Paul Debevec等 提出了与视点相关的纹理混合方法 。 也就是说 &#xff0c; 现实的漫游效果不是从摄像机的角度来看 &#xff0c; 但其仍然存在很多困难 。基于三维场景的视频融合 &#xff0c; 因其直观等特效在视频监控等相关领域有着…

Qt_day10_程序打包(完结)

目录 1. 设置图标 2. Debug和Release版本 3. 动态链接库 4. 打包 5. 联系项目要求 Qt开发的程序最终都是要给用户使用的&#xff0c;用户的电脑上不可能装一个Qt的开发环境导入项目使用。因此项目项目开发完成后需要打包——制作成安装包&#xff0c;用户直接下载并安装即可使用…

路径规划——RRT-Connect算法

路径规划——RRT-Connect算法 算法原理 RRT-Connect算法是在RRT算法的基础上进行的扩展&#xff0c;引入了双树生长&#xff0c;分别以起点和目标点为树的根节点同时扩展随机树从而实现对状态空间的快速搜索。在此算法中以两棵随机树建立连接为路径规划成功的条件。并且&…

【项目开发 | 跨域认证】JSON Web Token(JWT)

未经许可,不得转载。 文章目录 JWT设计背景:跨域认证JWT 原理JWT 结构JWT 使用方式注意JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,本文介绍它的原理、结构及用法。 JWT设计背景:跨域认证 互联网服务的用户认证流程是现代应用中的核心组成部分,通常的流程…

学习笔记——PLCT:milk-v duo(持续更新)

买板子 官方标配有可能是单板&#xff08;如下图&#xff09;无工具包&#xff0c;记得买之前问一下客服。

Kubernetes-ArgoCD篇-01-简介

1、什么是Argo CD Argo CD 是针对 Kubernetes 的声明式 GitOps 持续交付工具。 Argo CD官方文档地址&#xff1a;https://argo-cd.readthedocs.io Argo CD源码地址&#xff1a;https://github.com/argoproj/argo-cd 1.1 关于Argo Argo是一个开源的项目&#xff0c;主要是扩…

【Python】轻松实现机器翻译:Transformers库使用教程

轻松实现机器翻译&#xff1a;Transformers库使用教程 近年来&#xff0c;机器翻译技术飞速发展&#xff0c;从传统的基于规则的翻译到统计机器翻译&#xff0c;再到如今流行的神经网络翻译模型&#xff0c;尤其是基于Transformer架构的模型&#xff0c;翻译效果已经有了质的飞…

父子线程间传值问题以及在子线程或者异步情况下使用RequestContextHolder.getRequestAttributes()的注意事项和解决办法

用到的工具类&#xff1a; Slf4j Configuration Lazy(false) public class SpringContextUtil{public static HttpServletRequest getRequest() {ServletRequestAttributes servletRequestAttributes (ServletRequestAttributes) RequestContextHolder.getRequestAttributes()…

FRTC8563实时时钟芯片的作用

FRTC8563是NYFEA徕飞公司推出的一款实时时钟芯片&#xff0c;采用SOP-8封装形式。这种封装形式具有体积小、引脚间距小、便于集成等特点&#xff0c;使得FRTC8563能够方便地应用于各种电子设备中&#xff0c;如&#xff1a;安防摄像机、监控摄像机、行车记录仪、车载电子等。 F…

怎么样绑定域名到AWS(亚马逊云)服务器

1&#xff0c;拿着你买的域名去亚马逊申请一个证书。申请证书分两种&#xff0c;一种是去亚马逊后台填域名手动申请 &#xff0c;另一种是通过API来申请&#xff0c;类似如下代码&#xff1a; 2、证验证书。有两种方式&#xff1a;一种是通过邮件&#xff0c;另一种去到域名提供…

从0开始深度学习(28)——序列模型

序列模型是指一类特别设计来处理序列数据的神经网络模型。序列数据指的是数据中的每个元素都有先后顺序&#xff0c;比如时间序列数据&#xff08;股票价格、天气变化等&#xff09;、自然语言文本&#xff08;句子中的单词顺序&#xff09;、语音信号等。 1 统计工具 前面介绍…

边缘计算在工业互联网中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 边缘计算在工业互联网中的应用 边缘计算在工业互联网中的应用 边缘计算在工业互联网中的应用 引言 边缘计算概述 定义与原理 发展…

蓝桥杯——杨辉三角

代码 package day3;public class Demo2 {public static void main(String[] args) {// TODO Auto-generated method stub// for (int i 0; i < 10; i) {// for (int j 0; j < 10; j) {// System.out.print("外&#xff1a;"i"内&#xff1a;&qu…