我一直在阅读以下教程
http://www.obj-sys.com/asn1tutorial/node12.html
你能通过一个例子帮助我理解隐式标记吗?
实际上,在 ASN.1 中,标记有两个目的:键入和命名。键入意味着它告诉编码器/解码器是什么类型的数据类型(是字符串、整数、布尔值、集合等),命名意味着如果有多个相同类型的字段和一些(或全部)是可选的,它告诉en-/decoder该值是哪个字段。
如果您将 ASN.1 与 JSON 进行比较,您会看到以下 JSON 数据:
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor"
}
您会注意到,在 JSON 中,每个字段总是被显式命名(“Image”、“Width”、“Height”、“Title”)并且显式或隐式类型(“Title”是一个字符串,因为它的值被包围引号,“宽度”是一个整数,因为它没有引号,只有数字,它不是“null”,“true”或“false”,它没有小数点)。
在 ASN.1 中,这条数据将是:
Image ::= SEQUENCE {
Width INTEGER,
Height INTEGER,
Title UTF8String
}
这将在没有任何特殊标记的情况下工作,这里只需要通用标记。通用标签不命名数据,它们只是键入数据,因此 en-/decoder 知道前两个值是整数,最后一个是字符串。第一个整数是宽度,第二个是高度不需要在字节流中编码,它是由它们的顺序定义的(序列有固定的顺序,集合没有。在你提到的页面上集合是正在使用)。
现在更改架构如下:
Image ::= SEQUENCE {
Width INTEGER OPTIONAL,
Height INTEGER OPTIONAL,
Title UTF8String
}
好的,现在我们有一个问题。假设收到以下数据:
INTEGER(750), UTF8String("A funny kitten")
什么是750?宽度还是高度?可能是宽度(并且缺少高度)或可能是高度(并且缺少宽度),两者看起来都与二进制流相同。在 JSON 中,每条数据都被命名,而在 ASN.1 中则不是这样。现在只有一个类型是不够的,现在我们还需要一个名字。这就是非通用标签进入游戏的地方。将其更改为:
Image ::= SEQUENCE {
Width [0] INTEGER OPTIONAL,
Height [1] INTEGER OPTIONAL,
Title UTF8String
}
如果您收到以下数据:
[1]INTEGER(750), UTF8String("A funny kitten")
您知道 750 是高度而不是宽度(根本没有宽度)。在这里,您声明一个新标签(在这种情况下是特定于上下文的标签),它有两个目的:它告诉 en-/decoder 这是一个整数值(打字),它告诉它是哪个整数值(命名)。
但是隐式和显式标记之间有什么区别?不同之处在于隐式标记只是命名数据,编码/解码器需要隐式知道该名称的类型,而显式标记名称并显式键入数据。
如果标记是显式的,则数据将被发送为:
[1]INTEGER(xxx), UTF8String(yyy)
因此,即使解码器不知道 [1] 表示高度,它也知道字节“xxx”将被解析/解释为整数值。显式标记的另一个重要优点是将来可以更改类型而无需更改标记。例如
Length ::= [0] INTEGER
可以改为
Length ::= [0] CHOICE {
integer INTEGER,
real REAL
}
标签 [0] 仍然表示长度,但现在长度可以是整数或浮点值。由于类型是显式编码的,解码器将始终知道如何正确解码该值,因此这种更改是向前和向后兼容的(至少在解码器级别,不一定在应用程序级别向后兼容)。
如果标记是隐式的,则数据将被发送为:
[1](xxx), UTF8String(yyy)
不知道 [1] 是什么的解码器将不知道“xxx”的类型,因此无法正确解析/解释该数据。与 JSON 不同,ASN.1 中的值只是字节。所以“xxx”可能是一个、两个、三个或四个字节,如何解码这些字节取决于它们的数据类型,这在数据流本身中没有提供。此外,更改 [1] 的类型肯定会破坏现有的解码器。
好的,但是为什么有人会使用隐式标记?总是使用显式标记不是更好吗?使用显式标记,类型也必须在数据流中编码,这将需要每个标记两个额外的字节。对于包含数千个(甚至数百万个)标签的数据传输,可能每个字节都很重要(连接速度非常慢,数据包很小,丢包率很高,处理设备很弱),而且双方都知道所有自定义标签,为什么要浪费带宽,用于编码、传输和解码不必要的类型信息的内存、存储和/或处理时间?
请记住,ASN.1 是一个相当古老的标准,它旨在在网络带宽非常昂贵且处理器比现在慢数百倍的时代实现高度紧凑的数据表示。如果您查看当今所有的 XML 和 JSON 数据传输,甚至考虑为每个标签节省两个字节似乎都是荒谬的。
使用接受的答案作为编码示例:
Image ::= SEQUENCE {
Width INTEGER,
Height INTEGER,
Title UTF8String
}
编码的一个例子是:
内部序列分解为:
如果你有EXPLICIT OPTIONAL
价值观:
Image ::= SEQUENCE {
Width [0] EXPLICIT INTEGER OPTIONAL,
Height [1] EXPLICIT INTEGER OPTIONAL,
Title UTF8String
}
编码序列可能是:
30 15 A1 02 02 02 EE 0C 0E 41 20 66 75 6E 6E 79 20 6B 69 74 74 65 6E
(21 字节)内部序列分解为:
A1
02
02
02 EE
0C
0E
41 20 66 75 6E 6E 79 20 6B 69 74 74 65 6E