答案 0 :(得分:37)
在ASN.1标记中,实际上有两个目的:打字和命名。键入意味着它告诉en / /解码器是什么类型的数据类型(它是字符串,整数,布尔值,集合等),命名意味着如果有多个相同类型的字段和一些(或者所有这些都是可选的,它告诉en- /解码器该值是哪个字段。
如果你比较ASN.1,让我们说,JSON,你看看下面的JSON数据:
"Image": {
"Width": 800,
"Height": 600,
"Title": "View from 15th Floor"
}
你会注意到在JSON中,每个字段总是被明确命名(" Image"," Width"," Height","标题")并显式或隐式键入("标题"是一个字符串,因为它的值被引号括起来,"宽度"是一个整数,因为它没有引号,只有数字,它不是" null"," true"或" false",它没有小数句点。
在ASN.1中,这条数据将是:
Image ::= SEQUENCE {
Width INTEGER,
Height INTEGER,
Title UTF8String
}
这将在没有任何特殊标记的情况下工作,此处仅需要通用标记。 Universal tags没有命名数据,他们只是输入数据,所以en- / decoder知道前两个值是整数,最后一个是字符串。第一个整数是Width,第二个是Height,不需要在字节流中编码,它是由它们的顺序定义的(序列有一个固定的顺序,设置不是。在页面上你正在使用的是套装。)
现在按如下方式更改架构:
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- /解码器这是一个整数值(输入),它告诉它是哪个整数值(命名)。 / p>
但是隐式标记和显式标记有什么区别?不同之处在于隐式标记只是命名数据,en- /解码器需要隐式知道该名称的类型< / strong>,显式标记名称和明确键入数据。
如果标记是显式的,则数据将发送为:
[1]INTEGER(xxx), UTF8String(yyy)
所以即使解码器不知道[1]意味着高度,它也知道字节&#34; xxx&#34;要被解析/解释为整数值。显式标记的另一个重要优点是可以在将来更改类型而无需更改标记。 E.g。
Length ::= [0] INTEGER
可以更改为
Length ::= [0] CHOICE {
integer INTEGER,
real REAL
}
Tag [0]仍然表示长度,但现在长度可以是整数或浮点值。由于类型是明确编码的,解码器将始终知道如何正确解码值,因此这种变化是向前和向后兼容的(至少在解码器级别,不一定在应用程序级别向后兼容)。
如果隐式标记,则数据将以:
发送[1](xxx), UTF8String(yyy)
一个不知道[1]是什么的解码器,不会知道&#34; xxx&#34;的类型。因此无法正确解析/解释该数据。与JSON不同,ASN.1中的值只是字节。所以&#34; xxx&#34;可以是一个,两个,三个或可能是四个字节,如何解码这些字节取决于它们的数据类型,这在数据流本身中没有提供。同样改变[1]的类型肯定会破坏现有的解码器。
好的,但为什么有人会使用隐式标记?总是使用显式标记不是更好吗?使用显式标记时,类型也必须在数据流中进行编码,这将需要每个标记两个额外的字节。对于包含数千(甚至数百万)个标签的数据传输,可能每个字节都很重要(连接速度很慢,数据包很小,数据包损耗很大,处理设备非常弱),双方都知道所有自定义标签,为什么要浪费带宽用于编码,传输和解码不必要类型信息的存储器,存储器和/或处理时间?
请记住,ASN.1是一个相当古老的标准,它的目的是在网络带宽非常昂贵且处理器比现在慢几百倍的时候实现高度紧凑的数据表示。如果你看看今天的所有XML和JSON数据传输,甚至考虑为每个标签保存两个字节似乎是荒谬的。
答案 1 :(得分:2)
我发现this thread足够清晰,它还包含(小)例子甚至很难,它们是非常“极端”的例子。使用IMPLICIT标签的更“实际”的示例可以在this page中找到。
答案 2 :(得分:0)
以接受的答案作为编码示例:
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
750(2-字节) 0C
0E
41 20 66 75 6E 6E 79 20 6B 69 74 74 65 6E
“一只有趣的小猫”(14字节)