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

目录

1.应用层

再谈 "协议"

网络版计算器

[序列化 和 反序列化](#序列化 和 反序列化)

[2.重新理解 read、write、recv、send 和 tcp 为什么支持全双工](#2.重新理解 read、write、recv、send 和 tcp 为什么支持全双工)

Jsoncpp

特性

安装

序列化

反序列化

总结

Json::Value

[1. 构造函数](#1. 构造函数)

[2. 访问元素](#2. 访问元素)

[3. 类型检查](#3. 类型检查)

[4. 赋值和类型转换](#4. 赋值和类型转换)

[5. 数组和对象操作](#5. 数组和对象操作)


1.应用层

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

再谈 "协议"

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

其实,协议就是双方约定好的结构化的数据,因此我们后面会使用序列化与反序列化这种技术。

网络版计算器

这是我们后面要讲的一个tcp服务项目,我们这里先简单提一下。

例如, 我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端.那么这个数据要怎么传呢?我们可以简单看看以下两种方案。

约定方案一:

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

约定方案二:

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

从这两个方案我们明显能够感受到是我们的方案二更加标准可靠。

序列化 和 反序列化

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

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

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

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

所以:

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

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 中序列化和反序列化操作的详细介绍:

安装

bash 复制代码
C++
ubuntu:sudo apt-get install libjsoncpp-dev
Centos: sudo yum install jsoncpp-devel

序列化

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

  1. 使用 Json::Value 的 toStyledString 方法:
  • 优点:将 Json::Value 对象直接转换为格式化的 JSON 字符串。
  • 示例:
cpp 复制代码
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" : "男"
}

2.使用 Json::StreamWriter:

  • 优点:提供了更多的定制选项,如缩进、换行符等。
  • 示例:
cpp 复制代码
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" : "男"
}

3.使用 Json::FastWriter:

  • 优点:比 StyledWriter 更快,因为它不添加额外的空格和换行符。
  • 示例:
cpp 复制代码
#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:

  • 优点:提供详细的错误信息和位置,方便调试。
  • 示例:
cpp 复制代码
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: 北京

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

  • 在某些情况下,你可能需要更精细地控制解析过程,可以直接使用 Json::CharReader 的派生类。
  • 但通常情况下,使用 Json::parseFromStream 或 Json::Reader 的 parse 方法就足够了。

总结

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

Json::Value

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

1. 构造函数

  • Json::Value():默认构造函数,创建一个空的 Json::Value 对象。
  • Json::Value(ValueType type, bool allocated = false):根据给定的 ValueType(如 nullValue, intValue, stringValue 等)创建一个 Json::Value 对象。

2. 访问元素

  • 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 类型的键。

3. 类型检查

  • 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():检查值是否为对象(即键值对的集合)。

4. 赋值和类型转换

  • 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():将值转换为字符串类型(如果可能)。

5. 数组和对象操作

  • 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 类型的
相关推荐
柳鲲鹏4 小时前
多种方法:OpenCV中修改像素RGB值
前端·javascript·opencv·1024程序员节
wanhengidc4 小时前
传奇手游可以使用云手机挂机搬砖吗
服务器·arm开发·智能手机·玩游戏·1024程序员节
吴禅染4 小时前
爱思唯尔期刊投稿经验
1024程序员节
MATLAB代码顾问4 小时前
MATLAB 实现基于短时傅里叶变换 (STFT) 的音频信号时频分析与可视化
1024程序员节
拓端研究室4 小时前
专题:2025AI+直播+私域电商行业洞察报告|附200+份报告PDF、数据仪表盘汇总下载
1024程序员节
电棍2334 小时前
简单而管用的清理gpu显存的方法及分析
1024程序员节
新猿一马4 小时前
kafka组件traceId增强
1024程序员节
FinTech老王4 小时前
国产数据库替代MongoDB:政务电子证照新选择
mongodb·1024程序员节
Q741_1474 小时前
C++ 分治 快速选择算法 堆排序 TopK问题 力扣 215. 数组中的第K个最大元素 题解 每日一题
c++·算法·leetcode·分治·1024程序员节·topk问题·快速选择算法