# 自定义报头协议

> 在学习过计算机网络的课程,我们知道刚开始计算机都是单独脱机工作的,没有联网的情况下计算机的信息共享能力、运算能力都非常有限,后来诞生了计算机网络.有了就是那几网络,计算机 A 的信息和数据可以通过网络传递到计算机 B,同样计算机 A 可以获取到来自计算机 B 的数据. 但是不同计算机之间交换数据的时候就要通过网络来传输了.传输的过程中需要不同的计算机都遵循一定的规则来组装数据、传递信息,那么这样的规则就叫做协议.

## 1. 协议

计算机网络中有非常多协议,这些协议位于 OSI 的不同层中,比如 TCP/IP、UDP、SMTP、FTP 等. 协议之所以称为协议,是因为它具有约束效应,信息在端到端的传输过程中,同等层次之间通过使用同样的协议规则,这样发送方在该层次按照协议约定处理数据,接收方在该层次按照协议约定解析数据.成对存在.

![OSI七层模型](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/2019-10-07-OSI.jpg)
![TCP协议](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/2019-10--07-TCP.png)


## 2. 自定义协议

在日常开发的时候处于某些原因可能需要自定义报文协议.这个协议是建立在 TCP 连接的基础上,比如,移动端在做 APM 的时候将功能拆分为2个模块,一个是 APM 监控模块、一个为了方便可拓展单独做了一个数据上报组件,具有动态下发上报策略的配置.

所以上报组件这里涉及到和服务端高效通信,所以客户端和服务端约定了一套自定义的报文协议,如下所示.



- PACKET 整体结构
    | HEAD | META | BATCH_PAYLOAD |

- PACKET::HEAD 结构

    | META_SIZE (2bytes unsigned int) | BATCH_COUNT (1 bytes unsigned int) | PAYLOAD_SIZE (4bytes unsigned int) | PAYLOAD_SIZE (4bytes unsigned int) | ... |

- PACKET::META 结构

    换行符分割的 JSON 字符串

    crypto(deflate(JSON\nJSON...))

- PACKET::BATCH_PAYLOAD 结构

    JSON 体里每个字段的值都是 BASE64 编码的

    crypto(deflate(JSON) | crypto(deflate(JSON) | ...


其实计算机网络过程中传输的就是二进制数据,所以拿 iOS 举例来说,我们自定义报文协议的目的就是按照协议的约定,自定义组装好一个 NSData 的数据,报文里面的头规定了各种信息的组装格式.

 - HEAD 里面需要携带3个信息. 信息1:meta 的 size 信息,而且协议规定了必须使用 2byte 的 unsigned int 数据类型. 信息2: payload 报文的个数,必须使用 1byte 的 unsigned int 数据类型. 信息3:每个报文的大小,必须使用 4byte 的 unsigned int 数据类型.
 - META 里面需要携带1个信息. 将多条元数据用 "\n" 换行符拼接,另外拼接完的整体数据先压缩再加密
 - PAYLOAD 里面将每条数据先压缩,然后整体再加密

所以核心就上面的3点,一点都不难,只不过第一次做的时候可能会踩坑.列觉如下

- 协议明确规定了该采用什么样的数据类型,另外数据大小做了明确限制,如果你不这么做,服务端按照字节长度去解析数据就会出错,相应的客户端接口的返回结果就是失败.自讨苦吃
- 设计到数据的处理,就应该注意字节序的问题,比如 iOS 采用的小端序,网络规定数据传输使用大端序,假如你不知道这个问题,那么你很可能接口调用失败,所以你需要将你的数据转换为大端序的数据,关于大小端序的问题可以查看我的[这篇文章](https://github.com/FantasticLBP/knowledge-kit/blob/master/第五部分%20计算机网络/5.3.md)

Objective-C 语言中处理 unsigned int 的数据,所以你需要直接操作 unsign short 到 NSData, NSData 的接口很方便, `[NSData dataWithBytes:&metaLength length:sizeof(metaLength)]]` 就可以处理成 2byte 的 unsigned int 的数据
