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>
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
}
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
}
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
};
2
3
4
5
6
7
8
9
凡事可能存在特例,业界也有使用 xml 格式的协议,例如 XMPP,有兴趣的读者可以通过XMPP官网了解一下。