
目录1. 概念理解1.1 应用层1.2 再次理解“协议”1.3 序列化和反序列化1.3.1 概念1.3.2 举例1.3.3 为什么序列化反序列化是应用层协议的关键2. 重新理解read、write、recv、send及网络数据传输的本质3. tcp为什么支持全双工4.Jsoncpp详解 - 用于处理 JSON 数据序列化的 C 库4.1 Jsoncpp特性及安装4.2 Json:Value类4.3 JsonCpp 的四种 JSON 序列化方式5. 四种方式对比面试重点1. 使用 Json::Value 的toStyledString 方法2. 使用 Json::StreamWriter3. 使用 Json::FastWriter4. 使用 Json::StyledWriter writer;4.4 Json反序列化方式4.4 JsonCpp 的两种 JSON 反序列化方式1. 使用 Json::Reader2. 使用 Json::CharReader 的派生类(不推荐了上面的足够了)4.5 总结5. 实战案例 - 网络计算器1. 概念理解1.1 应用层应用层是直接面向用户需求的最上层它定义了应用程序之间通信的规则和接口目的是解决具体的业务问题比如网页浏览、文件传输、邮件收发。我们日常写的网络程序如 HTTP 接口服务、FTP 客户端、即时通讯工具、游戏联机模块其业务逻辑和用户交互相关的核心代码都属于应用层的范畴。1.2 再次理解“协议”协议是一种 “约定”。socket api的接口在读写数据时都是按 “字符串” 的方式来发送接收的。如果我们要传输一些 “结构化的数据” 怎么办呢? 其实协议就是双方约定好的结构化的数据1.3 序列化和反序列化1.3.1 概念序列化 反序列化的核心概念是应用层协议中 “结构化数据” 与 “网络传输字节流” 之间的转换操作—— 因为网络只能传输二进制字节流而应用程序要处理的是有业务含义的结构化数据比如你的聊天消息struct这两者的转换就靠这两个操作序列化把应用程序里的结构化数据如 C 的struct/class、JSON 对象转换成连续的字节流或字符串方便通过网络发送。反序列化把网络接收的字节流还原成应用程序能识别的结构化数据方便上层业务逻辑处理。1.3.2 举例以图中的 “聊天消息” 为例序列化过程发送端应用程序里的结构化数据是// 对应你图里的message/time/nicknamestructChatMsg{string message;// 你好啊string time;// 20xx-yy-zz aa:bb:ccstring nickname;// 新时代好青年};序列化就是把这个struct转换成连续的字符串字节流你好啊 20xx-yy-zz aa:bb:cc 新时代好青年—— 这一步完成了 “从多多个字段变一一个字节流”适配网络传输的要求。反序列化过程接收端接收端拿到字节流你好啊 20xx-yy-zz aa:bb:cc 新时代好青年后按照约定的格式拆分字段还原成ChatMsg结构体 —— 这一步完成了 “从一字节流变多多个业务字段”方便上层程序读取消息内容、显示到聊天界面。1.3.3 为什么序列化反序列化是应用层协议的关键你说 “应用层协议是两端能解析的约定”而序列化 / 反序列化就是这个 “约定” 的具体实现方式协议约定了 “聊天消息要包含 message/time/nickname 三个字段”序列化 / 反序列化约定了 “这三个字段怎么打包成字节流、怎么拆包”—— 两者结合才实现了两端的正确通信。2. 重新理解read、write、recv、send及网络数据传输的本质1.write/send、read/recv 的本质用户态与内核态的内存拷贝write()/send()发送将应用程序 “用户态内存” 中的数据拷贝到操作系统内核的 “TCP 发送缓冲区”并非直接发送到网络。read()/recv()接收将操作系统内核 “TCP 接收缓冲区” 中的数据拷贝到应用程序的 “用户态内存”并非直接从网络读取。核心这两个系统调用的本质是用户态与内核态之间的内存数据拷贝是应用程序与内核协议栈之间的 “数据交接” 操作。2.两台主机的网络通信“内核缓冲区 协议栈处理” 的完整流程两台主机的通信并非 “内核缓冲区直接互传”而是结合协议栈的分层处理主机 A应用层序列化后的数据 →write()拷贝到内核发送缓冲区→ TCP 将数据分段按 MSS 大小、封装 TCP 头 → 网络层封装 IP 头 → 链路层封装帧头 → 通过物理网络发送。主机 B链路层接收帧、解包 → 网络层解 IP 头 → TCP 验证报文合法性、重组分段数据 → 将完整数据存入内核接收缓冲区→ 应用层通过read()拷贝到用户态内存 → 反序列化处理。3.TCP 对内核缓冲区数据的控制逻辑内核缓冲区中数据的 “发送时机”“分段大小” 由 TCP 协议主导但 “内容总大小” 由应用层决定TCP 决定发送时机如 Nagle 算法合并小数据、拥塞控制动态调整发送速率数据分段大小基于 MSS 最大报文段长度避免 IP 分片应用层决定要发送的业务数据的总长度如你例子中聊天消息的字符串长度。4.TCP 的 “传输控制”出错后的纠错机制TCP 被称为 “传输控制协议”核心是通过以下机制解决传输错误差错校验通过 TCP 头的校验和检测报文是否损坏超时重传发送端未收到确认时重传发送缓冲区中的数据快速重传接收端收到乱序报文时主动反馈触发发送端重传流量控制通过滑动窗口避免接收端缓冲区溢出导致数据丢失。5.TCP 全双工的本质“单连接 独立双缓冲对”TCP 通信是全双工双向同时通信核心原因是单个 TCP 连接的两端都各自拥有 “独立的发送缓冲区 接收缓冲区”—— 即主机 A 的 “发送缓冲 接收缓冲”与主机 B 的 “发送缓冲 接收缓冲” 是双向独立的数据流通道支持两端同时发送、同时接收数据。6.网络数据传输的本质“多轮拷贝 分层协议处理”网络传输的完整本质是应用层数据 → 「用户态→内核态」拷贝write → 内核协议栈分层封装TCP→IP→链路层 → 物理网络传输 → 对端分层解包 → 「内核态→用户态」拷贝read → 应用层处理。核心是“用户态 - 内核态拷贝”“协议栈的封装 / 解包”“物理层的信号传输”的组合流程。3. tcp为什么支持全双工TCP 通信是全双工双向同时通信核心原因是单个 TCP 连接的两端都各自拥有 “独立的发送缓冲区 接收缓冲区”—— 即主机 A 的 “发送缓冲 接收缓冲”与主机 B 的 “发送缓冲 接收缓冲” 是双向独立的数据流通道支持两端同时发送、同时接收数据。4.Jsoncpp详解 - 用于处理 JSON 数据序列化的 C 库4.1 Jsoncpp特性及安装Jsoncpp 是一个用于处理 JSON 数据的 C 库。它提供了将 JSON 数据序列化为字符串以及从字符串反序列化为 C 数据结构的功能。Jsoncpp 是开源的广泛用于各种需要处理 JSON 数据的 C 项目中。特性简单易用Jsoncpp 提供了直观的 API使得处理 JSON 数据变得简单。高性能Jsoncpp 的性能经过优化能够高效地处理大量 JSON 数据。全面支持支持 JSON 标准中的所有数据类型包括对象、数组、字符串、数字、布尔值和 null。错误处理在解析 JSON 数据时Jsoncpp 提供了详细的错误信息和位置方便开发者调试。安装ubuntusudoapt-getinstalllibjsoncpp-dev Centos:sudoyuminstalljsoncpp-devel4.2 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::ValuedefaultValue Json::nullValue)在对象中插入或访问一个元素如果键不存在则使用默认值。Json::Value operator[](const std::string key, const Json::ValuedefaultValue Json::nullValue)同上但使用 std::string 类型的。4.3 JsonCpp 的四种 JSON 序列化方式JSON 序列化将Json::Value对象转换为 JSON 格式的字符串方便网络传输、文件存储等。1. Json::Value::toStyledString()概念Json::Value类提供的成员函数用于将 JSON 对象转换为带缩进、换行的格式化字符串便于阅读。函数原型std::string toStyledString() const;参数及返回值无参数。返回值std::string表示序列化后的 JSON 字符串。特点 使用简单适合调试和打印 JSON 数据。2. Json::StreamWriter概念 JsonCpp 提供的流式 JSON 序列化接口可以将 JSON 数据写入输出流支持自定义格式。核心函数int write(const Json::Value root, Json::OStream* sout);参数及返回值root需要序列化的 JSON 对象。sout输出流指针指定 JSON 数据写入的位置。返回值int表示写入结果通常成功返回0。特点StreamWriter是抽象类通常通过Json::StreamWriterBuilder创建具体写入器可以配置缩进等格式选项。3. Json::FastWriter概念 JsonCpp 提供的快速序列化类将 JSON 对象转换为紧凑的字符串默认不添加美化缩进。核心函数std::string write(const Json::Value root);参数及返回值root需要序列化的 JSON 对象。返回值std::string表示序列化后的 JSON 字符串。特点 输出紧凑减少不必要的空白字符适合网络传输等场景。4. Json::StyledWriter概念 JsonCpp 提供的格式化序列化类将 JSON 对象转换为带缩进和换行的字符串方便阅读。核心函数std::string write(const Json::Value root);参数及返回值root需要序列化的 JSON 对象。返回值std::string表示格式化后的 JSON 字符串。特点 输出格式美观适合调试、日志和配置文件存储。5. 四种方式对比面试重点序列化方式输出特点适用场景toStyledString()格式化字符串简单调试StreamWriter格式可配置写入输出流实际项目开发FastWriter紧凑字符串网络传输StyledWriter格式化字符串日志、调试 大白话理解toStyledString()直接把 JSON 整理成漂亮的字符串。StreamWriter功能更灵活可以设置格式并指定输出位置。FastWriter尽量去掉多余空白让 JSON 更紧凑。StyledWriter专门把 JSON 整理得更容易阅读。面试补充FastWriter和StyledWriter属于旧版 Writer 接口现代 JsonCpp 更推荐使用StreamWriterBuilder配合StreamWriter可以灵活控制是否缩进、如何格式化等。1. 使用 Json::Value 的toStyledString 方法优点将Json::Value 对象直接转换为格式化的JSON字符串。示例如下#includeiostream#includestring#includejsoncpp/json/json.h// 引入 jsoncpp 库的核心头文件该库用于 C 中 JSON 数据的构建与解析intmain(){// 创建 Json::Value 类型的根对象这是 jsoncpp 库的核心数据结构// 可理解为一个空的 JSON 容器用于存储键值对形式的 JSON 数据Json::Value root;// 向 JSON 根对象中添加键值对root[name]joe;root[sex]男;// 调用 Json::Value 的 toStyledString() 方法// 将 JSON 对象转换为带缩进、换行的格式化字符串易读形式而非紧凑的单行字符串std::string sroot.toStyledString();// 将格式化后的 JSON 字符串输出到控制台std::coutsstd::endl;return0;}$ ./test.exe{name:joe,sex:男}2. 使用 Json::StreamWriter优点提供了更多的定制选项如缩进、换行符等。示例如下#includeiostream#includestring// 引入字符串流头文件用于在内存中构建/存储JSON字符串替代物理文件流#includesstream// 引入智能指针头文件用于安全管理StreamWriter对象的内存自动释放#includememory// 引入jsoncpp库核心头文件提供JSON构建/序列化相关类#includejsoncpp/json/json.hintmain(){// 1. 构建JSON数据对象核心数据载体// 创建Json::Value根对象用于存储键值对形式的JSON数据Json::Value root;// 向JSON根对象添加键值对root[name]joe;root[sex]男;// 2. 构建JSON序列化器StreamWriter// 创建StreamWriterBuilder对象它是jsoncpp中生成StreamWriter的工厂类// 相比toStyledString()StreamWriterBuilder更灵活可自定义序列化格式如缩进、换行Json::StreamWriterBuilder wbuilder;// 通过工厂类创建StreamWriter对象并使用unique_ptr智能指针管理// unique_ptr会自动释放内存避免手动delete导致的内存泄漏是C现代内存管理的最佳实践std::unique_ptrJson::StreamWriterwriter(wbuilder.newStreamWriter());// 3. 内存中序列化JSON将JSON对象写入字符串流// 创建stringstream字符串流对象在内存中存储序列化后的JSON字符串无需写入文件std::stringstream ss;// 调用StreamWriter的write方法将Json::Value对象序列化为JSON字符串写入stringstream// 第一个参数要序列化的JSON数据对象第二个参数输出流这里是内存字符串流sswriter-write(root,ss);// 4. 输出结果// 调用stringstream的str()方法获取内存中的JSON字符串并打印到控制台std::coutss.str()std::endl;return0;}$ ./test.exe{name:joe,sex:男}3. 使用 Json::FastWriter优点比 StyledWriter 更快因为它不添加额外的空格和换行符。示例如下#includeiostream#includestring// 引入jsoncpp库核心头文件提供JSON构建和序列化相关的类#includejsoncpp/json/json.hintmain(){// 1. 构建JSON数据对象// 创建Json::Value根对象作为JSON数据的载体存储键值对形式的JSON数据Json::Value root;// 向JSON根对象添加键值对root[name]joe;root[sex]男;// 2. 创建FastWriter序列化器对象// Json::FastWriter是jsoncpp中专门用于生成紧凑版JSON字符串的类// 相比toStyledString()的美化版FastWriter生成的字符串无缩进、无换行体积更小适合网络传输/数据存储Json::FastWriter writer;// 3. 序列化JSON对象为字符串// 调用FastWriter的write()方法将Json::Value对象转换为紧凑的JSON字符串std::string swriter.write(root);// 4. 输出序列化后的JSON字符串到控制台std::coutsstd::endl;return0;}$ ./test.exe{name:joe,sex:男}4. 使用 Json::StyledWriter writer;#includeiostream#includestring// 引入jsoncpp库核心头文件提供JSON数据构建、序列化的核心类如Json::Value、StyledWriter#includejsoncpp/json/json.hintmain(){// 1. 构建JSON数据载体核心对象// 创建Json::Value类型的根对象可理解为一个空的JSON容器用于存储键值对形式的JSON数据Json::Value root;// 向JSON根对象添加第一个键值对root[name]joe;root[sex]男;// 2. 创建StyledWriter序列化器对象// Json::StyledWriter是jsoncpp中专门用于生成美化版JSON字符串的类// 相比FastWriter的紧凑版StyledWriter会自动添加缩进、换行让JSON字符串更易读适合调试/日志输出Json::StyledWriter writer;// 3. 将JSON对象序列化为美化版字符串// 调用StyledWriter的write()方法把Json::Value对象转换为带缩进/换行的JSON字符串std::string swriter.write(root);// 4. 输出序列化后的JSON字符串到控制台std::coutsstd::endl;return0;}$ ./test.exe{name:joe,sex:男}4.4 Json反序列化方式反序列化指的是将序列化后的数据重新转换为原来的数据结构或对象。Jsoncpp提供了以下方法进行反序列化4.4 JsonCpp 的两种 JSON 反序列化方式JSON 反序列化将 JSON 格式的字符串解析为Json::Value对象方便程序读取和操作数据。1. Json::Reader概念 JsonCpp 提供的传统 JSON 解析类用于将 JSON 字符串或输入流中的数据解析为Json::Value对象。核心函数boolparse(conststd::stringdocument,Json::Valueroot,boolcollectCommentstrue);参数及返回值document待解析的 JSON 字符串。root输出参数用于存储解析后的 JSON 数据。collectComments是否收集 JSON 中的注释默认为true。返回值bool解析成功返回true失败返回false。特点 使用简单但属于旧版解析接口现代 JsonCpp 更推荐使用CharReaderBuilder。2. Json::CharReader 的派生类概念Json::CharReader是 JsonCpp 提供的抽象解析接口通常通过Json::CharReaderBuilder创建具体解析器实现 JSON 字符串的反序列化。核心函数boolparse(constchar*beginDoc,constchar*endDoc,Json::Value*root,std::string*errs);参数及返回值beginDoc指向 JSON 字符串的起始位置。endDoc指向 JSON 字符串的结束位置末尾后一个位置。root输出参数存储解析后的 JSON 数据。errs输出参数用于保存解析失败时的错误信息。返回值bool解析成功返回true失败返回false。特点 支持通过CharReaderBuilder配置解析规则灵活性更高适合实际项目开发。3. 两种方式对比面试重点反序列化方式特点适用场景Json::Reader接口简单使用方便旧项目、基础学习Json::CharReader可配置解析规则扩展性强现代 C 项目开发 大白话理解Reader老式 JSON 翻译器直接把 JSON 字符串翻译成程序可以操作的数据。CharReader新式 JSON 翻译器可以通过CharReaderBuilder设置解析规则更加灵活。面试补充 实际开发中更推荐使用Json::CharReaderBuilder它既可以创建CharReader解析器也可以配合Json::parseFromStream()直接解析输入流。一句话总结Reader和CharReader都用于 JSON 反序列化前者是传统接口后者是更灵活的现代接口。1. 使用 Json::Reader优点提供详细的错误信息和位置方便调试。示例如下#includeiostream#includestring// 引入jsoncpp库核心头文件提供JSON解析Reader和数据存储Value的核心类#includejsoncpp/json/json.hintmain(){// 1. 定义待解析的JSON格式字符串// 注意C中双引号需要用反斜杠转义\该字符串包含name字符串、age整数、city字符串三个键值对std::string json_string{\name\:\张三\, \age\:30,\city\:\北京\};// 2. 创建JSON解析器对象// Json::Reader是jsoncpp中专门用于解析JSON字符串/文件的类负责将JSON格式的文本转换为Json::Value对象Json::Reader reader;// 3. 创建Json::Value对象作为解析后的数据载体// Json::Value可以理解为一个万能容器能存储字符串、整数、数组、对象等各种JSON数据类型Json::Value root;// 4. 执行JSON解析操作// 调用Reader的parse()方法将JSON字符串解析到root对象中// 返回值为bool类型true表示解析成功false表示解析失败如JSON格式错误boolparsingSuccessfulreader.parse(json_string,root);// 5. 解析错误处理核心避免非法访问解析失败的空数据if(!parsingSuccessful){// 解析失败时输出格式化的错误信息包含错误位置、原因便于调试std::coutFailed to parse JSON: reader.getFormattedErrorMessages()std::endl;return1;// 返回非0值表示程序异常退出}// 6. 从解析后的Json::Value对象中提取数据std::string nameroot[name].asString();// asString()将Json::Value中存储的字符串类型数据转换为C std::stringintageroot[age].asInt();// asInt()将Json::Value中存储的整数类型数据转换为C intstd::string cityroot[city].asString();// asString()提取city字段的字符串值// 7. 输出解析后的结果验证数据正确性std::coutName: namestd::endl;std::coutAge: agestd::endl;std::coutCity: citystd::endl;return0;}$ ./test.exe Name: 张三 Age:30City: 北京2. 使用 Json::CharReader 的派生类(不推荐了上面的足够了)在某些情况下你可能需要更精细地控制解析过程可以直接使用 Json::CharReader 的派生类。但通常情况下使用 Json::parseFromStream 或Json::Reader 的parse 方法就足够了。4.5 总结toStyledString 、StreamWriter 和FastWriter 提供了不同的序列化选项你可以根据具体需求选择使用。Json::Reader 和parseFromStream 函数是Jsoncpp中主要的反序列化工具它们提供了强大的错误处理机制。在进行序列化和反序列化时请确保处理所有可能的错误情况并验证输入和输出的有效性。5. 实战案例 - 网络计算器网络计算器实现 - 自定义套接字序列化守护进程-CSDN博客