AMF 格式是 Adobe 推出的一个二进制数据格式,说实在话,我更愿意用 MsgPack 或 JSON。不过这里不讨论这一点。我们来讨论一下一个完整的 AMF 格式数据。说实在话,我能说 Adobe 的官方文档写的很烂吗?让人看着简直是天书一样!
AMF 0 :http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/amf/pdf/amf0-file-format-specification.pdf
AMF 3 :http://download.macromedia.com/pub/labs/amf/amf3_spec_121207.pdf
我们讨论 AMF 格式的数据时,要注意 AMF 格式分成了两个部分:打包协议和内部数据格式协议。我们在网上看到的 AMF 3 标准文档实际上是内部的数据格式协议,并没有包括打包的头部内容。
我们今天来讨论下这个打包的头部内容先:
00000000 00 03 00 00 00 01 00 1d 45 78 61 6d 70 6c 65 53 ExampleS 00000010 65 72 76 69 63 65 2f 72 65 74 75 72 6e 4f 6e 65 ervice/returnOne 00000020 50 61 72 61 6d 00 02 2f 31 00 00 00 3d 0a 00 00 Param /1 = 00000030 00 01 11 0a 0b 0d 6d 79 54 79 70 65 11 61 72 72 myType arr 00000040 61 79 56 61 6c 09 07 01 04 01 04 02 06 07 65 72 ayVal er 00000050 74 13 73 74 72 69 6e 67 56 61 6c 06 07 62 6c 61 t stringVal bla 00000060 0d 69 6e 74 56 61 6c 04 02 intVal
- 两个字节的版本标记:00 03,也可能是 00 00,BE 编码。
- 两个字节的头部小节数量:00 00 ,BE编码。如果不为00,则后面跟具体的头部小节信息。这里为0,每个小节的格式为:
[小节名称:[16位BE编码长度][UTF-8编码字符串内容]][8位布尔值, must-understand 标记][32位BE编码小节长度][AMF 0 值] - 两个字节的消息内容数据数量:00 01,BE编码。这里 00 01 转换后的结果就是 1,也就是说这个头部表明有 1 个数据结点。接下来是多个数据体列表,每个数据体的格式如下:
(1)、请求的目标服务名称:[16位BE编码字符串长度][UTF-8编码字符串内容];
(2)、服务响应名称:[16位BE编码字符串长度][UTF-8编码字符串内容];
如上面请求的目标服务名称部分为:00 1d 45 78 61 6d 70 6c 65 53 65 72 76 69 63 65 2f 72 65 74 75 72 6e 4f 6e 65 50 61 72 61 6d,00 1d 就是名称的长度 29,后面 29 个 UTF-8 编码的字符构成实际的值:ExampleService/returnOneParam。同理,后面的响应名称 00 02 2f 31 对应的是长度为 2 字节的字符串 /1。
(3)、32位BE编码的的数据体长度 00 00 00 3d ,也就是说本数据体占用的空间是 61 字节。
(4)、AMF 0 格式的数据体内容。
这里注意一个问题,AMF 数据打包是按照 AMF 0 标准来打包的,所以虽然头部版本号是 00 03,头部和消息的数据体依然是默认按 AMF 0 来开始解析,直到你遇到了类型为 11 (十进制 17 )的类型标记,以它标记的数据才是 AMF 3 格式的。我们以上面的数据内容做一下人工解析:
0a 00 00 00 01 11 0a 0b 0d 6d 79 54 79 70 65 11 61 72 72 61 79 56 61 6c 09 07 01 04 01 04 02 06 07 65 72 74 13 73 74 72 69 6e 67 56 61 6c 06 07 62 6c 61 0d 69 6e 74 56 61 6c 04 02 01
- 0a :AMF 0 数组(10)
- 00 00 00 01 :数组元素数量为 1 ;
- 11 :切换成 AMF 3 对象(17)
- 0a :自身类型为 AMF 3 对象(10)
- 0b:一个 AMF 3 整数类型,用于做对象标志位(11,二进制 1011:[类型属性数量:25位][动态类型标志][外部类型标志][内置类声明标志][内置对象标志]),这是一个动态、内置类声明的动态类型。剩下的位数为0,所以这个类型没有子类型属性。如果有,则根据 AMF3 的字符串规则,读取每一个属性名称字符串。
- 0d:一个 AMF 3 整数类型,用于做类型名称长度(13,根据AMF 3 字符串规则,实际长度为 6 字节)
- 6d 79 54 79 70 65:UTF-8 编码的类型名称(myType)
- 11 61 72 72 61 79 56 61 6c :属性名称的 AMF 3 字符串(arrayVal)
- 09:arrayVal 属性的值类型为 AMF 3 数组
- 07:一个 AMF 3 整数类型,用于做数组标志位,最低位是数组是否内置标志,剩下的是实际的普通数组元素个数(3),用字符串做键值的数组元素不在此列。
- 01:数组元素的键值为 0 长度的 AMF 3 字符串(低位是引用标志位),如果不为0,需要循环读取直到内容字符串内容为空。
- 04 01:元素类型为整数值 01
- 04 02:元素类型为整数值 02
- 06 07 65 72 74:AMF3 字符串 ert(后面不再详解)
- 13 73 74 72 69 6e 67 56 61 6c:属性名称为 AMF 3 字符串 stringVal
- 06 07 62 6c 61 :AMF 3 字符串 bla
- 0d 69 6e 74 56 61 6c:属性名称为 AMF 3 字符串 intVal
- 04 02 :元素内容为整数值 02
好了,这就是上面这段 AMF 数据的格式分析,大家看明白了吗?如果不明白,就当我没说好了。