AMF学习总结(二)--AMF协议
1 AMF协议
AMF是Action Message Format协议的简称,AMF协议是Adobe公司自己的协议,主要用于数据交互和远程过程调用,在功能上相当于WebService,但是AMF与WebService中传输XML不同的是,AMF传输的是二进制数据,而XML是文本数据,AMF的传输效率比XML高。AMF使用HTTP方式传输,目前主要是用于ActionScript中,即实现Flex和Server之间的通信。
AMF最大的特色在于可直接将Flash内置对象,例如Object, Array, Date, XML,传回服务器端,并且在服务器端自动进行解析成适当的对象,这就减轻了开发人员繁复工作,同时也更省了开发时间。由于AMF采用二进制编码,这种方式可以高度压缩数据,因此非常适合用来传递大量的资料。数据量越大,Flash Remoting的传输效能就越高,远远超过WebService。至于XML, 它们使用纯文本的传输方式,效能就更不能与Flash Remoting相提并论了。除了AMF编码进行高效数据操作的功能之外,ByteArray还有一个很酷的功能,就是从内存中深层次的Copy(Clone)整个对象。
AMF协议是封包协议,HTTP协议是网络协议,具体的区别这里就不多描述,可以百度搜索ISO网络协议七层模型。
AMF协议是基于Http协议的.它的内容处理过程大致是这样:
-
从客户端获取Http请求(Request)流.
-
对流进行解串行化(Deserialize),得到服务器端程序能够识别的数据,并建立一个响应(Response)消息。
-
对流进行各种处理(记录、许可、服务)得到返回值。
-
对响应流进行串行化。
-
发送Http响应给客户端。
1.1 AMF0与AMF3
AMF包括两种数据格式:AMF0和AMF3,其中AMF0是基本的消息格式,但是后来Adobe对AMF0进行了优化,开发了扩展的AMF3,即AMF3并不是AMF0的完全替代,使用AMF3需要在外面套一层AMF0做为容器。
AMF引进于2001年FlashPlayer6,并且在引入AS2.0的FlashPlayer7和FlashPlayer8中没有任何改变。这个版本的AMF参考与AMF0.在FlashPlayer9中,AS3.0和新的AS虚拟机(AVM+)一起被引进,新的数据类型和语言特性使AMF升级成为可能,给了一个发布新的AMF版本的机会,新版本的AMF在序列化数据的时候做了一些优化,使得编码格式去除了一些冗余信息,升级后的AMF版本便是AMF3.1 // AMF0数据类型; 2 typedef enum 3 { 4 AMF_NUMBER = 0x00, // 数字(double); 8字节表示 5 AMF_BOOLEAN = 0x01, // 布尔; 1字节整数表示 6 AMF_STRING = 0x02, // 字符串; 2字节表示 7 AMF_OBJECT = 0x03, // 对象; 8 AMF_MOVIECLIP = 0x04, // 保留,未使用; 9 AMF_NULL = 0x05, // null; 10 AMF_UNDEFINED, = 0x06 // 未定义; 11 AMF_REFERENCE = 0x07, // 引用; 12 AMF_ECMA_ARRAY = 0x08, // 数组; 13 AMF_OBJECT_END = 0x09, // 对象结束; 3个字节 14 AMF_STRICT_ARRAY = 0x0a, // 严格的数组; 15 AMF_DATE = 0x0b, // 日期; 16 AMF_LONG_STRING = 0x0c, // 长字符串; 4字节表示 17 AMF_UNSUPPORTED = 0x0d, // 未支持; 18 AMF_RECORDSET = 0x0e, // 保留,未使用; 19 AMF_XML_DOC = 0xof, // xml文档; 20 AMF_TYPED_OBJECT = 0x10, // 有类型的对象; 21 AMF_AVMPLUS = 0x11, // 需要扩展到AMF3; 22 AMF_INVALID = 0xff // 无效的; 23 }AMFDataType; 24 25 // AMF3数据类型; 26 typedef enum 27 { 28 AMF3_UNDEFINED = 0x00, // 未定义; 29 AMF3_NULL = 0x01, // null; 30 AMF3_FALSE = 0x02, // false; 31 AMF3_TRUE = 0x03, // true; 32 AMF3_INTEGER = 0x04, // 数字int; 33 AMF3_DOUBLE = 0x05, // double; 34 AMF3_STRING = 0x06, // 字符串; 35 AMF3_XML_DOC = 0x07, // xml文档; 36 AMF3_DATE = 0x08, // 日期; 37 AMF3_ARRAY = 0x09, // 数组; 38 AMF3_OBJECT = 0x0a, // 对象; 39 AMF3_XML = 0x0b, // xml; 40 AMF3_BYTE_ARRAY = 0x0c // 字节数组; 41 } AMF3DataType;
1.3 字节序
字节序是指数据在计算机内存中的存储顺序,主要有大端(Big-Endian)和小端(Little-Endian)两种方式。
大端字节序中,数据的高位字节存放在低地址,而低位字节存放在高地址;小端字节序则相反,数据的高位字节存放在高地址,低位字节存放在低地址1。Intel的处理器通常采用小端字节序,而Moto的处理器则使用大端字节序。
网络协议通常采用网络字节顺序,即Big-Endian字节序。这样,无论发送方和接收方的计算机体系结构如何,都可以正确的解释和传输数据。
AMF0采用的也是大端字节序。
1 int val = 0x3489; 2 3 big endian: 4 低地址 0------->1------->2 高地址 5 +--------+--------+ 6 | 0x34 | 0x89 | 7 +--------+--------+ 8 little endian: 9 +--------+--------+ 10 | 0x89 | 0x34 | 11 +--------+--------+
1.4 具体定义及示例
1.4.1 Number类型
类型0x00(1字节) + 值(8字节)
例:00 40 10 00 00 00 00 00 00
number类型:0x00
number值: 0x40 0x10 0x00 0x00 0x00 0x00 0x00 0x00 (为大端存储)
在解number的值的时候要先正过来才求值,注意:这个值是double类型的。
Java方式解析如下:
1 public static double convertDoubleFromBigEndian(byte[] bigEndianBytes) { 2 // 使用ByteBuffer进行字节序转换 3 ByteBuffer buffer = ByteBuffer.allocate(8); 4 buffer.put(bigEndianBytes); 5 buffer.flip(); // 反转缓冲区 6 return buffer.getDouble(); // 获取转换后的double值 7 } 8 public static void main(String[] args) { 9 byte[] byteArray = {0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 10 double value = convertDoubleFromBigEndian(byteArray); 11 // 输出转换后的值 输出应该为:4.0 12 System.out.println("小端 double 值为: " + value); 13 }
1.4.2 Bool类型
类型0x01(1字节)+值(1字节)
例如:01 01
类型:0x01
值 :0x01(true),0x00(false)
1.4.3 String类型
类型0x02(1字节)+长度(2字节)+值(前面指定长度的字节)
例:02 00 08 73 68 61 6E 67 67 75 61
类型:0x02
长度:0x00 0x08(大端存储)
值 :0x73, 0x68, 0x61, 0x6E, 0x67, 0x67, 0x75, 0x61(大端存储)Java方式解析如下:
public static Object parse(byte[] data) throws IOException { try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data))) { return readAMF0Data(dis); } } private static Object readAMF0Data(DataInputStream dis) throws IOException { byte type = dis.readByte(); switch (type) { case 0: // Number return dis.readDouble(); case 1: // Boolean return dis.readBoolean(); case 2: // String int length = dis.readUnsignedShort(); byte[] bytes = new byte[length]; dis.readFully(bytes); return new String(bytes, "UTF-8"); case 3: // Object // 实际解析过程中需要递归调用readAMF0Data来解析对象内部的字段 throw new UnsupportedOperationException("AMF0 Object is not supported"); case 4: // MovieClip case 5: // Null case 6: // Undefined case 7: // Reference case 8: // MixedArray case 9: // EndOfObject case 10: // Array case 11: // Date case 12: // LongString case 13: // Unsupported throw new UnsupportedOperationException("AMF0 type " + type + " is not supported"); default: throw new IOException("Invalid AMF0 type: " + type); } } public static void main(String[] args) { byte[] byteArray = {0x02, 0x00, 0x08, 0x73, 0x68, 0x61, 0x6E, 0x67, 0x67, 0x75, 0x61}; try { Object result = parse(byteArray);
//输出的值为shanggua
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
1.4.4 Object类型
Object由三部分组成
类型0x03(1字节)
第一部分(Property):Property结构(默认String类型,省略0x02):长度(2字节)+值(前面指定长度的字节数)
第二部分(Value):Value结构,可以是以上说明的任意类型
第三部分(结尾):固定为 00 00 09
例:00 03 61 70 70 02 00 09 6c 69 76 65 2f 31 32 33 34
Property长度:00 03
Property值: 61 70 70 对应为字符为“app”
Value类型:02
Value长度:00 09
Value值:6c 69 76 65 2f 31 32 33 34 对应字符为“live/1234”
1.4.5 MovieClip
类型0x04(1字节)
视频剪辑,官方未提供支持
1.4.6 Null类型
类型0x05(1字节)1.4.7 Undefined类型
类型0x06
就一个字节0x06
1.4.8 引用类型
类型0x07 + 引用的复合类型索引(匿名对象、特定类型对象、数组或混合数组,BE编码)
1.4.9 复合数组
类型0x08 +[元素数量](4个字节)+ [元素*元素数量]
该类型是出现在Object下的一个参数data中, 格式解析参照Object,例子如下
1.4.10 Object End(对象结束类型)
类型0x09(3个字节)
例:00 00 09
1.4.11 严格数组
类型0x0a + [元素数量](4个字节) +[元素*元素数量]
元素数量为 32 位无符号整数,后面跟每个元素的定义,与复合数组不同,它没有额外的索引内容
1.4.12 日期类型
类型0x0b(一个字节)+ double(8个字节,表示从1970-1-1到表示的时间所经过的毫秒数) + ushort(2个字节,无符号整数表示时区)
例:0b 64 65 73 63 72 69 70 74 69 6f 6e
1.4.13 长字符串
类型0x0c + 字符串长度(4个字节)+ 字符串内容
1.4.14 不支持的类型
类型0x0d
官方未提供支持
1.4.15 数据集类型
类型0x0e
官方未提供支持
1.4.16 XML
类型0x0f + 内容长度(4个字节) + 内容
1.4.17 特定类型对象
类型0x10 + [类型名称](2个字节) + [对象类型-对象值]
1.4.18 AMF3对象
类型0x11
AMF3数据类型,这个类型定义到对象类型定义结束,都是AMF3格式
1.4.19 注意Rtmp数据中的坑
在Wireshark抓包中会出现一个特殊的字节C3
这个是AMF0消息中的Connect,但是在TCP payload中会多出一个C3(这个字节是是客户端发送Connect命令的时候会多出的一个字节)
但是在Wireshark解析中会自动过滤掉这个字节,如下图(此处作者未验证,作者不是使用的Wireshark抓包):
建议:在解析rtmp数据的时候不要一味地看Wireshark解析,要学会从TCP payload的实际数据中解析数据。
1.5 AMF消息流(AMF Message)
AMF消息流指的是通过抓包工具抓取的一个完整的数据包(package),里面包含了版本号、头部、消息体等数据,头部和消息体里面用到的数据使用AMF的格式来进行存储。
1.6 AMF服务端支持
在开发Flex应用程序的时候,根据后端服务器使用的不同情况,目前可以支持AMF的编程语言如下表
.Net | Java | PHP | Ruby On Rail | Python | Curl | ColdFusion |
BlazeDS.net | BlazeDS | AMFPHP | PyAMF | Curl | CodeFusion | |
Fluorine | Granite data services | Zend | RubyAMF | |||
WebORB | WebORB | WebORB | WebORB | WebORB |
2 参考资料
https://www.pianshen.com/article/210955594/
https://blog.51cto.com/jawsy/392476
https://blog.csdn.net/qq_28309121/article/details/104599058