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协议的.它的内容处理过程大致是这样:

  1. 从客户端获取Http请求(Request)流.

  2. 对流进行解串行化(Deserialize),得到服务器端程序能够识别的数据,并建立一个响应(Response)消息。

  3. 对流进行各种处理(记录、许可、服务)得到返回值。

  4. 对响应流进行串行化。

  5. 发送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.2 AMF数据类型
   基础知识,1字节=8位(byte),16进制代表4位(byte)
 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字节)
  例:05
  类型:0x05
  null类型很特殊就一个字节0x05

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

  

 

posted @ 2024-11-11 16:02  子非鱼220  阅读(10)  评论(0编辑  收藏  举报