CppGuide社区 CppGuide社区
首页
  • 最新谷歌C++风格指南(含C++17/20)
  • C++17详解
  • C++20完全指南
  • C++23快速入门
  • C++语言面试问题集锦
  • 🔥C/C++后端开发常见面试题解析 (opens new window)
  • 网络编程面试题 (opens new window)
  • 网络编程面试题 答案详解 (opens new window)
  • 聊聊WebServer作面试项目那些事儿 (opens new window)
  • 字节跳动面试官现身说 (opens new window)
  • 技术简历指南 (opens new window)
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 从零用C语言写一个Redis
  • Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go开发实用指南
    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
  • Rust编程

    • Rust编程指南
  • 数据库

    • SQL零基础指南
    • MySQL开发与调试指南
GitHub (opens new window)
首页
  • 最新谷歌C++风格指南(含C++17/20)
  • C++17详解
  • C++20完全指南
  • C++23快速入门
  • C++语言面试问题集锦
  • 🔥C/C++后端开发常见面试题解析 (opens new window)
  • 网络编程面试题 (opens new window)
  • 网络编程面试题 答案详解 (opens new window)
  • 聊聊WebServer作面试项目那些事儿 (opens new window)
  • 字节跳动面试官现身说 (opens new window)
  • 技术简历指南 (opens new window)
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 从零用C语言写一个Redis
  • Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go开发实用指南
    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
  • Rust编程

    • Rust编程指南
  • 数据库

    • SQL零基础指南
    • MySQL开发与调试指南
GitHub (opens new window)
  • 第1章高频C++11重难点知识解析

  • 第2章Linux GDB高级调试指南

  • 第3章C++多线程编程从入门到进阶

  • 第4章C++网络编程重难点解析

  • 第5章网络通信故障排查常用命令

  • 第6章高性能网络通信协议设计精要

    • 6.1 TCP 协议是流式协议
    • 6.2 如何解决粘包问题
    • 6.3 解包与处理
    • 6.4 从 struct 到 TLV——协议的演化历史
    • 6.5 整型数值的压缩
    • 6.6 通信协议设计时的注意事项
    • 6.7 包分片
    • 6.8 跨语言之间的网络通信协议识别与解析
    • 6.9 xml 与 json 格式协议
    • 6.10 一个自定义协议示例
    • 6.11 http 协议
    • 6.12 SMTP、POP3 协议与邮件客户端
    • 6.13 WebSocket 协议
  • 第7章高性能服务结构设计

  • 第8章Redis 网络通信模块源码分析

  • 第9章后端服务重要模块设计探索

  • C++后端开发进阶
  • 第6章高性能网络通信协议设计精要
zhangxf
2023-04-05

6.9 xml 与 json 格式协议

xml 和 json 这两种格式由于其良好的自我解释性,是开发中使用的非常广泛的两种数据格式。一个 xml 格式的示例如下所示:

<?xml version="1.0" encoding="utf-8"?>
<SkinList>
  <Skin>
    <SkinID>0</SkinID>
    <SkinName>默认皮肤</SkinName>
    <SkinPath>Skin0\</SkinPath>
  </Skin>
  <Skin>
    <SkinID>1</SkinID>
    <SkinName>冰凉清爽</SkinName>
    <SkinPath>Skin1\</SkinPath>
  </Skin>
  <CurSkinID>0</CurSkinID>
</SkinList>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

一个 json 格式的示例如下:

{
    "userid": 1001,
    "username": "xiaofang",
    "nickname": "小方",
    "facetype": 0,
    "gender": 1,
    "birthday": 19900101,
    "signature": "生活需要很多的力气呀。xx",
    "clienttype": 1
}
1
2
3
4
5
6
7
8
9
10

关于 xml 和 json 的格式介绍,不是本节的重点内容,如果还不熟悉的读者可以去网络上查找相关的资料来学习一下。

那么 xml 和 json 格式可以单独用来作为网络通信协议吗?

当然可以,但是一般单独使用 xml 或者 json 格式作为网络通信协议的服务非常少,其原因是在给数据包分界得到一个个完整的包时非常不方便。

如果读者理解了前面 《TCP 协议是流式协议》和《如何解决粘包问题》章节中介绍内容,可能已经知道答案了。无论是 xml 还是 json 格式,如果单独作为协议,一般由于业务数据内容不同,每个包的长度不一样。那么在 TCP 流式数据中只能采取固定结束符的方式来分割,对于不确定长度的 xml 和 json,在频繁进行数据交换的网络通信程序中,每次解包前都得遍历一次 xml 或 json 字符串以寻找特定的包结束符。例如,对于上面的 xml 示例,就是在流式数据中寻找 </SkinList> 字符串以作为一个 xml 格式的包结束符,对于上面的 json 格式,仅仅寻找 json 结束的花括号(})容易造成误判的,还得在这基础上加上其他限定标记,例如,json 的最后一个节点可以加一个特殊字段标志作为结束标记,以上述 json 为例,在其末尾增加一个 endFlag 标志后变成如下形式:

{
    "userid": 1001,
    "username": "xiaofang",
    "nickname": "小方",
    "facetype": 0,
    "gender": 1,
    "birthday": 19900101,
    "signature": "生活需要很多的力气呀。xx",
    "clienttype": 1,
    "endFlag": 0
}

1
2
3
4
5
6
7
8
9
10
11
12

这样当该 json 作为一个数据包时,在判断包结束标志时可以通过寻找 "“endFlag”: 0" 加一个 “}” 这样的字符串作为包分界符号。但这种方法很不灵活,json 在某些系统或库中解析时各个字段的位置顺序可能会被调整,也就是说,像 "“endFlag”: 0" 这样的字段可能会被调整到 json 中的非末尾位置;另外 json 被格式化后某些字段值后面会被追加 “\n” 或 “\r\n” 这样的换行符,这就给程序寻找指定包结束符带来了困扰。

所以通常情况下,xml 或者 json 格式不会被单独作为协议格式,而是会作为某个协议的一部分出现,例如如下格式:

struct msg
{
	//在消息头header中说明整个包的大小,减去header、cmd和seq的大小就是buf的长度
	//即是xml或者json的长度
	msgheader header;
	int32_t	  cmd;
	int32_t   seq;
	char*     buf;	//buf是一个字符串,其格式可以是一个 xml 或者 json
};
1
2
3
4
5
6
7
8
9

凡事可能存在特例,业界也有使用 xml 格式的协议,例如 XMPP,有兴趣的读者可以通过XMPP官网了解一下。

上次更新: 2025/05/19, 16:52:22
6.8 跨语言之间的网络通信协议识别与解析
6.10 一个自定义协议示例

← 6.8 跨语言之间的网络通信协议识别与解析 6.10 一个自定义协议示例→

最近更新
01
第二章 关键字static及其不同用法
03-27
02
第一章 auto与类型推导
03-27
03
第四章 Lambda函数
03-27
更多文章>
Copyright © 2024-2025 沪ICP备2023015129号 张小方 版权所有
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式