2 基础知识
& 注释:
本章的内容主要翻译自《ASN.1 Communication between Heterogeneous Systems》。
2.8 基本类型
Table 2-4 基本类型汇总表
类型 | UNIVERSAL Tag | 取值 |
BOOLEAN | 1 | TRUE,FALSE |
NULL | 5 | NULL |
INTEGER | 2 | 整数 |
ENUMERATED | 10 | 类型定义中列出的成员 |
REAL | 9 | 实数 |
BIT STRING | 3 | 比特串 |
OCTET STRING | 4 | 八位组串,字节流 |
OBJECT IDENTIFIER | 6 |
|
RELATIVE-OID | 13 |
|
2.8.2 BOOLEAN类型
是两个值的集合:TRUE,FALSE。如下:
RoundResult ::= BOOLEAN
ok RoundResult ::= TRUE
ko RoundResult ::= FALSE
UNIVERSAL的Tag值为1。
2.8.3 NULL类型
空类型,只有一个值NULL。作为一种结果,空类型是传输报告和响应的典型情况。
Ack ::= NULL
它也在时间信息中使用,表示传送时间没有赋值,如:
Clock ::= CHOICE
{
time UTCTime,
out-of-order NULL
}
当时钟电池没有电时,值为:
battery-down Clock ::= out-of-order:NULL
NULL也可以用在表示链表结束的空接点,如:
Figure 2-3 链表结构示意图
LinkedList ::= SEQUENCE
{
data Data,
next CHOICE
{
linked-list LinkedList,
end NULL
}
}
当然,可以使用SEQUENCE OF来达到类似目的:
LinkedList ::= SEQUENCE OF Data
UNIVERSAL的Tag值为5。
2.8.4 INTEGER类型
整数,可以是正整数也可以是负整数,取值范围在协议中没有界定。所以必须由编解码器提前确定好整数的范围,分配必要的内存空间来处理。可以通过在描述中增加一个值声明来判断:
Interval ::= INTEGER(123456789..123456790)
在使用PER编解码规则时,会按照本地整数范围进行截断;相比之下BER编码规则中因为有明确的长度,所以不会出现截断的情况。
虽然在数学可以有“-0”,但在ASN.1描述中不能这样描述。
一些情况下,比如为了定义错误码,需要给一些数值特定的名字,可以使协议更易于理解,也改善了应用层和编解码器之间的接口。这些信息当然可以用注释给出,但是ASN.1编译器不会使用这些信息,在词法分析开始,这些就都被忽略了。ASN.1为INTEGER类型提供了一种特殊语法来解决这个问题。以软盘驱动器的错误码为例:
ErrorCode ::= INTEGER
{
disk-full(1),
no-disk(-1),
disk-not-formatted(2)
}
stupid-error ErrorCode ::= disk-full
这些名字只能被用于定义ErrorCode类型的值,不能用于其它类型值的定义。
命名的整数不需要排序。对于没有命名的整数,还是可以使用的;已经命名的整数,也可以直接使用数字:
ok ErrorCode ::= 0
stupid-error ErrorCode ::= 1
注意:这种命名使用的使花括号,不是约束(使用圆括号)。
这种方式和ENUMERATED类型相比,区别在于:
l 必须指定数值,不能像ENUMERATED中那样缺省;
l 不能使用扩展标记“…”。当然这实际上不是个问题,和ENUMERATED不同,没有命名的整数还可以使用。
此外,这种方式如果使用不当,会造成混乱,要特别小心。如:
alpha INTEGER ::= 1
Type1 ::= INTEGER { alpha(2) }
Type2 ::= INTEGER { alpha(3), beta(alpha) }
gamma Type2 ::= beta
delta Type2 ::= alpha
实际上gamma的值为1,delta的值为3。
注意:在ASN.1中,INTEGER和REAL类型之间没有兼容性,就是说不能将INTEGER的值赋给一个REAL类型的值。同样的,INTEGER和ENUMERATED类型间也不存在兼容性。
UNIVERSAL的Tag值为2。
2.8.5 ENUMERATED类型
ENUMERATED类型与INTEGER类型相比, 区别在于:
l 取值是受限制的,没有列出的数值不能使用;
l 数值是不能操作的,只能用于编码;
l 不用明确指定数值,ASN.1可以自动计算相应的数值(从0开始);
l 可以使用扩展,保证和新版本的编码兼容;
l 是数值关联(显式或者隐式)到标识符,而INTEGER中则是标识符显式关联到数值。
ABRT-diagnostic ::= ENUMERATED
{
no-reason-given(1),
protocol-error(2),
authentication-mechanism-name-not-recognized(3),
authentication-mechanism-name-required(4),
authentication-failure(5),
authentication-required(6),
...
}
不能直接使用数字,如:
abert-diagnostic ABERT-diagnostic ::= 4 -- 错误的!
在使用扩展标记“...”时,需要额外注意的是,在扩展标记后的成员,其数值必须是升序排列的。
A ::= ENUMERATED {a, b, ..., c(0)}
-- 错误: a 和 c 都是0
B ::= ENUMERATED {a, b, ..., c, d(2)}
-- 错误: c 和 d 都是2
C ::= ENUMERATED {a, b(3), ..., c(1)}
-- Correct: c = 1
和CHOICE、SEQUENCE和SET不同,在使用扩展标记“...”后,增加新成员时,ENUMERATED类型中不需要使用版本标识“[[ ]]”,并且只能有一个扩展标记。
注意事项:
l 按照ASN.1的语义模型,任意两个ENUMERATED类型都是不兼容的。
l 当一个ENUMERATED类型被IMPORTS到另一个模块中,则其列表中的标识符也被导入,但只能用于该类型值的赋值。
l 基本列表(在扩展标识符之前)中的值不需要连续,也不需要排序。
l 扩展列表(在扩展标识符之后)中的值不需要连续,但需要升序排列。
l 扩展列表中的值不能和基本列表中的值重复,不论该值是明确指定的,还是缺省计算的。
l 扩展列表中成员的值必须该列表中前一个成员的值大。
l 表示成员值的必须是数字,不能是标识符。
UNIVERSAL的Tag值为10。
2.8.6 REAL类型
从语义上等效于:
SEQUENCE
{
mantissa INTEGER (ALL EXCEPT 0),
base INTEGER (2|10),
exponent INTEGER
}
但是REAL类型有自己特定的UNIVERSAL Tag值。
pi REAL ::= {314159, 10, -5}
e REAL ::= {271828128459045235360287, 10, -23}
zero REAL ::= 0
注意:实数0不能用三元组的方式给出。
我们用PLUS-INFINITY和MINUS-INFINITY分别表示“正无穷大”和“负无穷大”,这两个也不能用三元组表示。
此外,我们也要主要到ASN.1中的实数只能以2或者10为底(base的约束)。
UNIVERSAL的Tag值为9。
2.8.7 BIT STRING类型
按照ASN.1的定义,BIT STRING可以长度为0,也可以非常长。它可以是原始的比特流形式,也可以是布尔向量模型。
比特流形式:
pi-decimals BIT STRING ::=
'00100100001111110110101010001000100001
01101000110000100011010011000100110001
100110001010001011100000001101110000'B
pi-decimals BIT STRING ::=
'243F6A8885A308D313198A2E0370'H
注意:这种形式下,要考虑大小端的影响。
布尔向量模型:
如Figure 2-4所示的权限示意图,当使用不同的比特表示权限时,在ASN.1中可以这样描述:
Rights ::= BIT STRING
{
user-read(0),
user-write(1),
group-read(2),
group-write(3),
other-read(4),
other-write(5)
}
group1 Rights ::= { group-read, group-write }
当然,也可以之间用码字来赋值:
group2 Rights ::= '0011'B
group2 Rights ::= '3'H
group3 Rights ::= '001100'B
weird-rights Rights ::= '0000001'B
注意:
l 命名的比特列表不影响值的声明;
l 命名的比特列表没有限制,位置也不必连续,也不对类型的长度产生影响。如weird-rights。
l 以码字赋值时,起始的比特是必须的(最左面的比特),用来定位。后续的0比特无意义。group2和group3 与group1的结果是一样的。
需要对BIT STRING长度进行约束时,可以使用SIZE,如:
RIGHTS ::= BIT STRING (SIZE(6))
和命名INTEGER类型相似,在命名BIT STRING中也不能使用扩展标记“…”。
和OCTET STRING类型一样,可以使用CONTAINING 和ENCODED BY来约束。
UNIVERSAL的Tag值为3。
2.8.8 OCTET STRING类型
与BIT STRING类型类似,但是单位不是比特而是8个比特的八位组。
UNIVERSAL的Tag值为4。
2.8.9 OBJECT IDENTIFIER类型
OBJECT IDENTIFIER类型是对象的唯一标识的集合,这里对象可以是(但不限于):
l 一种抽象语法,一定是在表示层无歧义的注册过(ISO8822);
l 一种传输语法,一定是注册过,在表示层可以被抽象语法使用(ISO8823-1);
l 一种应用实体(ISO7498-3);
l 一个ASN.1模块;
l 一种ROSE操作(ISO9072-2);
l 等等。
我们用注册树来表征所有注册的标识:
Figure 2-5 注册树
OBJECT IDENTIFIER类型就是该注册树所有叶子的路径集合。一个OBJECT IDENTIFIER类型的值可以是:
internet-id OBJECT IDENTIFIER ::=
{ iso(1) identified-organization(3) dod(6) internet(1) }
francetelecom-id OBJECT IDENTIFIER ::=
{ iso member-body f(250) type-org(1) ft(16) }
ber-id OBJECT IDENTIFIER ::= { 2 1 1 }
即可以用各叶子的名字,可以用编号,也可以混合使用。
此外,对于多个有相同根名字的值,可以如下定义:
ID ::= OBJECT IDENTIFIER
id-edims ID ::= { joint-iso-itu-t mhs-motif(6) edims(7) }
id-bp ID ::= { id-edims 11 }
id-bp-edifact-ISO646 ID ::= { id-bp 1 }
id-bp-edifact-T61 ID ::= { id-bp 2 }
id-bp-edifact-octet ID ::= { id-bp 3 }
id-bp-ansiX12-ISO646 ID ::= { id-bp 4 }
id-bp-ansiX12-T61 ID ::= { id-bp 5 }
id-bp-ansiX12-ebcdic ID ::= { id-bp 6 }
每当一个对象注册到注册树中时,ISO标准ISO9834-1都会对它增加一个相应的文本描述,称之为ObjectDescriptor。如对上面ber-id的ObjectDescriptor为:
ber-descriptor ObjectDescriptor ::=
"Basic Encoding of a single ASN.1 type"
2.8.10 RELATIVE-OID类型
前面OBJECT IDENTIFIER类型可以看作是对象在注册树中的绝对路径,而RELATIVE-OID类型则是相对路径。其参考基准节点可以是一个OBJECT IDENTIFIER类型的值(动态的),或者是收发双方默认的(静态的)。
RELATIVE-OID可以使用CONSTRAINED BY进行约束。
2.9 字符串类型
Known-multiple Character String Type指字符串中每个字符编码所占字节数都一样。
Table 2-5 字符串类型一览表
类型名字 | Tag | 字符表 | ESC | Multi |
NumericString | 18 | 字符“0”到“9”,空格 |
| 是 |
PrintableString | 19 | 字符“A” 到 “Z”,“a”到“z”,“0”到“9”,空格,单引号(’),圆括号(( ,)),加号(+),逗号(,),减号(-),点(.),斜杠(/),冒号(:),等号(=),问号(?) |
| 是 |
VisibleString ISO646String | 26 | [ISOReg] entry no. 6; space |
| 是 |
IA5String | 22 | [ISOReg] entry no. 1 & 6; space, delete |
| 是 |
TeletexString T61String | 20 | [ISOReg] entry no. 6, 87, 102, 103, 106, 107, 126, 144, 150, 153, 156, 164, 165, 168; space, delete | 是 |
|
VideotexString | 21 | [ISOReg] entry no. 1, 13, 72, 73, 87, 89, 102, 108, 126, 128, 129, 144, 150, 153, 164, 165, 168; space, delete | 是 |
|
GraphicString | 25 | all the graphical sets (called `G') of [ISOReg]; space | 是 |
|
GeneralString | 27 | all the graphical sets (called `G') and all the control characters (called `C') of [ISOReg]; space, delete | 是 |
|
UniversalString | 28 | [ISO10646-1] |
| 是 |
BMPString | 30 | the basic multilingual plane [ISO10646-1] (65,536 cells) |
| 是 |
UTF8String | 12 | [ISO10646-1] |
|
|
2.10 组合类型与扩展原则
2.10.1 Tag
传输数据,一定要避免接收方收到数据解码时出现不清楚的地方。具体说,接收方要明确知道每个收到数据的类型,我们就要对各类型进行系统性的编号。在ASN.1的第一种编解码规则BER(Basic Encode Rule)中,对数据编码使用的是三元组规则:<type, length, value>或者称为TLV,这里type或者称为tag,它是类型的标识符,在ASN.1中每种类型的tag是唯一的。length是编码数据value的字节数。value是实际数据的编码。
I. Tags与Tagging Classes
分配给每个类型的tag实际上是一个值对:<tagging class, number>。有四种tagging class:UNIVERSAL,context-specific,APPLICATION和PRIVATE。这种方式实际提供了不同的Tag空间。在一个上下文中,如果Tagging Class不同,则相同number的两个Tag也是不同的。
l UNIVERSAL的Tag是ASN.1标准定义的,在描述中不能修改。
Table 2-6 ASN.1中定义的UNIVERSAL类Tag
Tag | 类型 |
0 | BER保留 |
1 | BOOLEAN |
2 | INTEGER |
3 | BIT STRING |
4 | OCTET STRING |
5 | NULL |
6 | OBJECT IDENTIFIER |
7 | ObjectDescripion |
8 | EXTERNAL, INSTANCE OF |
9 | REAL |
10 | ENUMERATED |
11 | EMBEDDED PDV |
12 | UFT8String |
13 | RELATIVE-OID |
14 | 保留 |
15 | 保留 |
16 | SEQUENCE, SEQUENCE OF |
17 | SET, SET OF |
18 | NumericString |
19 | PrintableString |
20 | TeletexString, T61String |
21 | VideotexString |
22 | IA5String |
23 | UTCTime |
24 | GeneralizedTime |
25 | GraphicString |
26 | VisibleString, ISO646String |
27 | GeneralString |
28 | UniversalString |
29 | CHARACTER STRING |
30 | BMPString |
31 | 保留 |
l context-specific的Tag是对SEQUENCE、SET和CHOICE及其成员使用的,可以在描述中自己定义,只要不产生歧义,相同数值可以在不同结构中反复使用。如:
A-possible-type ::= SET
{
integer [0] CHOICE
{
a [0] INTEGER,
b [1] INTEGER
},
boolean [1] CHOICE
{
a [0] BOOLEAN,
b [1] BOOLEAN
}
}
l APPLICATION的Tag是为了“to define a data type that finds wide, scattered use within a particular application and that must be distinguishable (by means of its [ abstract syntax]) from all other data types used in the application”。在APPLICATION作用域内,其Tag也是唯一的。如:
Order-number ::= [APPLICATION 0] NumericString (SIZE (12))
但是因为使用IMPORTS等方式下,很难保证唯一性,所以这种Tag类已经不推荐使用了。
l PRIVATE的Tag是为了“use a private-use Tagged [ type] to define a data type that finds use within a particular organization or country and that must be distinguishable (by means of its [ abstract syntax]) from all other data types used by that organization or country”。如一个公司可能这样扩展传输层PDU:
RejectTPDU ::= SET
{
destRef [0] Reference,
yr-tu-nr [1] TPDUnumber,
credit [2] Credit,
extended [PRIVATE 0] BOOLEAN DEFAULT FALSE
}
PRIVATE类的Tag现在也不推荐使用了。
II. Tagging Mode
使用context-specific类Tag,如下:
Afters ::= CHOICE
{
cheese [0] IA5String,
dessert [1] IA5String
}
当没有其它更多描述信息,并且使用BER编码时,会将UNIVERSAL类和context-specific类的Tag同时编码出来。这种方式成为EXPLICIT模式。在下面这种情况:
T1 ::= [0] INTEGER
变更为:
T2 ::= [0] CHOICE
{
integer INTEGER,
real REAL
}
当CHOICE取值为integer时不需要变更。
与之相对应的时IMPLICIT模式,它将只编码context-specific类的Tag直到另一个UNIVERSAL类Tag出现。如:
T ::= [1] IMPLICIT T1
T1 ::= [5] IMPLICIT T2
T2 ::= [APPLICATION 0] IMPLICIT INTEGER
对于类型T,只有Tag值 1被编码。
这种模式需要接收方能保证清楚知道抽象语法,能正确解码。
III. Global Tagging Mode
可以在模块定义时,声明模块全局Tag模式。可以是EXPLICIT TAGS、IMPLICIT TAGS和AUTOMATIC TAGS。
l IMPLICIT TAGS,模块内所有SEQUENCE、SET和CHOICE的成员都是IMPLICIT模式(除非它是CHOICE类型、开放类型或者一个参数类型)。但不影响IMPORTS的内容。
l AUTOMATIC TAGS,模块内所有SEQUENCE、SET和CHOICE类型ASN.1编译器会自动从0开始,步长为1进行自动编码。而其中的成员则用IMPLICIT模式,除非它是CHOICE类型、开放类型或者一个参数类型。
下面两个定义是等效的:
M DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
T ::= SEQUENCE
{
a INTEGER,
b CHOICE
{
i INTEGER,
n NULL
},
c REAL
}
END
M DEFINITIONS ::=
BEGIN
T ::= SEQUENCE
{
a [0] IMPLICIT INTEGER,
b [1] EXPLICIT CHOICE
{
i [0] IMPLICIT INTEGER,
n [1] IMPLICIT NULL
},
c [2] IMPLICIT REAL
}
END
如果使用全局AUTOMATIC TAGS模式时,在描述中给一个类型指定了一个Tag,那么对这个类似的AUTOMATIC TAGS就关闭。如果一个SEQUENCE或者SET类型包含COMPONENTS OF条目,而且这个条目在自动编号前展开;但自动标号又要求在展开前进行,ASN.1编译器要自己检查这种冲突。
对新的协议现在都推荐使用AUTOMATIC TAGS。
2.10.2 SEQUENCE
SEQUENCE是最常用的ASN.1类型之一,它可以嵌套使用,还可以使用OPTIONAL、DEFAULT和COMPONENTS OF等对成员进行修饰。
一个常见的SEQUENCE声明如:
Description ::= SEQUENCE
{
surname IA5String,
first-name IA5String,
age INTEGER
}
该类型的一个值为:
johnny Description ::=
{
surname "Smith",
first-name "John",
age 40
}
使用OPTIONAL和DEFAULT时,SEQUENCE类型应该能被明确解码,不存在模棱两可的地方。如下这个类型就有问题:
T ::= SEQUENCE
{
x INTEGER,
y INTEGER OPTIONAL,
z INTEGER DEFAULT 0,
t INTEGER
}
在使用BER编码规则时,对于值t1 T::={x 1, y 2, t 3}而言,接收方收到的码流如下:
Figure 2-6 OPTIONAL引起的歧义
那么在解码过程中,V为2的值无法确定是成员y还是成员z。这种情况下,该类型的定义应该在Tag上做一些补充:
T ::= SEQUENCE
{
x INTEGER, -- [UNIVERSAL 2]
y [0] INTEGER OPTIONAL,
z [1] INTEGER DEFAULT 0,
t INTEGER -- [UNIVERSAL 2]
}
注意:对于接收方,没有收到声明DEFAULT的成员和收到DEFAULT的值是等价的。
在类型定义中,可以使用COMPONENTS OF将另一个类型的成员插入到当前类型中:
Registration ::= SEQUENCE
{
COMPONENTS OF Description,
marital-status ENUMERATED
{single, married, divorced,widowed}
}
COMPONENTS的效果使得上面的定义等效于:
Registration ::= SEQUENCE
{
surname IA5String,
first-name IA5String,
age INTEGER,
marital-status ENUMERATED
{single, married, divorced, widowed}
}
而不是:
Registration ::= SEQUENCE
{
description Description,
marital-status ENUMERATED
{single, married, divorced,widowed}
}
使用COMPONENTS OF的好处在于,如上例中,Description更新升级时,Registration的内容不需要手工修改。
2.10.3 SET
和SEQUENCE相比,SET的成员是没有顺序的。同样可以使用OPTIONAL、DEFAULT和COMPONENTS OF。
2.10.4 SEQUENCE OF
SEQUENCE OF相当于某些语言中的动态数组或者列表:所有成员都是一个类型,数目不定。如:
PariTierce DEFINITIONS ::=
BEGIN
SteepleChase ::= SEQUENCE OF INTEGER
END
相应的典型值为:
winningCombination SteepleChase ::= {5, 2, 12}
no-one-arrived SteepleChase ::= { }
ASN.1提供一种元标记来编写和描述项目的注释、文档,以“@”开头,后接模块名称、类型名字成员序号或者“*”(表示全部),如:
-- @PariTierce.SteepleChase.* must be distinct
-- @PariTierce.SteepleChase.0 = 5 (horse no. 5 always wins!)
如果是在模块内,则“@”和模块名都可以省略,如:
-- SteepleChase.* must be distinct
-- SteepleChase.0 = 5 (horse no. 5 always wins!)
2.10.5 SET OF
和SEQUENCE OF相比,SET OF的成员是顺序的,其它类似。
2.10.6 CHOICE
CHOICE类型表示一种选择,类似UNION。该提供两个信息:被选择的选项,相应的值。也就是说对于类型:
Afters ::= CHOICE
{
cheese [0] IA5String,
dessert [1] IA5String
}
它的一个值而言:
mine Afters ::= dessert:"profiteroles"
(注意:表示方法先是选择的选项dessert,而后是“:”,最后是值 )
没有必要先定义一个枚举类型来表征具体的选项。
和SEQUENCE、SET类型不同,CHOICE没有缺省的UNIVERSAL Tag值,因为它是一些类型的集合,其中被选择项目的Tag会被作为CHOICE相关Tag。在和SEQUENCE、SET一起使用时,要注意使用AUTOMATIC TAGS或者context-specific Tags。如下面,类型U的定义有问题(Tag 0 和 1都重复了两次):
T ::= CHOICE { a [0] INTEGER,b [1] NULL }
U ::= SET
{
x [0] REAL,
y T,
z CHOICE
{
c [1] BIT STRING,
d [2] OCTET STRING
}
}
U的定义应该修改为类似下面:
U ::= SET
{
x [0] REAL,
y [1] T,
z [2] CHOICE
{
c [1] BIT STRING,
d [2] OCTET STRING
}
}
在协议中,CHOICE类型经常出现。应用层间交换的PDU,通常都是CHOICE结构的,如ROSE(Remote Operation Service Element -ISO13712-1)的简化:
ROS ::= CHOICE
{
invoke [1] Invoke,
returnResult [2] ReturnResult,
returnError [3] ReturnError,
reject [4] Reject
}
某些时候,我们想重用CHOICE类型中的某些选项的类型,此时需要使用在ASN.1中被称为选择符(Selection)的 符号“<”。如:
Element ::= CHOICE
{
atomic-no INTEGER (1..103),
symbol PrintableString
}
MendeleievTable ::= SEQUENCE SIZE (103) OF symbol < Element
einsteinium symbol < Element ::= "Es"
2.10.7 类型扩展
可以在定义中插入扩展标记“…”来定义一个类型是可扩展的。在ASN.1中可扩展的类型有ENUMERATED、SEQUENCE、SET和CHOICE。INTEGER和BIT STRING类型,基于前面的介绍,是隐含可扩展类型(对于类似ENUMERATED方式,未命名的数字和比特位置都可以使用)。如:
State ::= ENUMERATED {on, off, out-of-order, ...}
Description ::= SEQUENCE
{
surname IA5String,
first-name IA5String,
age INTEGER,
...
}
Dimensions ::= SET
{
x INTEGER,
y INTEGER,
...
}
Afters ::= CHOICE
{
cheese IA5String,
dessert IA5String,
...
}
按照标准,扩展标记可以有两个,他们将类型定义分割为两个扩展部分:
T ::= SEQUENCE
{
a A,
b B,
...,
...,
c C
}
在扩展时,新增的项目可能有多个,并且其中可能还带有OPTIONAL和DEFAULT。这种情况下,解码器在解码中就会遇到困难。为了区分不同版本,在扩展部分,可以使用版本符号“[[ ]]”,如:
Afters ::= CHOICE
{
cheese IA5String,
dessert IA5String,
...!ExtensionPb:greedy,
[[coffee NULL ]], -- version 2
[[cognac IA5String]] -- version 3
}
Figure 2-7 相同协议不同扩展之间接力示例
如果扩展部分只有一个成员,则版本符号可以省略。如:
State ::= ENUMERATED {on, off, out-of-order, ..., stand-by} -- version 2
Dimensions ::= SET
{
x INTEGER,
y INTEGER,
...,
z INTEGER -- version 2
}
Afters ::= CHOICE
{
cheese IA5String,
dessert IA5String,
...,
coffee NULL -- version 2
}
而类型T的新定义中:
T ::= SEQUENCE
{
a A,
b B,
...,
[[d D,
e E]],
...,
c C
}
d、e放在一个版本符号中,说明在T类型的一个值中,一个出现则另一个必须也出现。
当解码扩展类型值时,我们会发现:
l 没有预期的扩展或者
l 出现不期望的扩展
此时,解码器会产生一个协议错误,并且按照应用设计方指定的具体动作来操作:通常是忽略不期望扩展或者为预期的扩展赋缺省值;同时解码器可能还会以事先约定的信号通知应用层。这个约定的信号就是异常Exception,在抽象语法中以“!”开头,后接一个值(可以是数、字符串等),放在扩展标记之后。至于应当如何应对异常,就不是ASN.1关心的内容了。如:
Description ::= SEQUENCE
{
surname IA5String,
first-name IA5String,
age INTEGER,
...!extended-description
}
extended-description INTEGER ::= 1
Dimensions ::= SET
{
x INTEGER,
y INTEGER,
... !IA5String:"dimension error"
}
Afters ::= CHOICE
{
cheese IA5String,
dessert IA5String,
...!ExtensionPb:greedy,
coffee NULL,
cognac IA5String
}
ExtensionPb::= ENUMERATED {greedy, ...}
应用扩展时需要注意的规则:
l 如果模块在头声明中使用了AUTOMATIC TAGS,而且组合类型的根(即类型入口)没有指明Tag,则自动编码过程中,先对根进行自动Tag编码,再对扩展部分进行Tag编码。
l 如果根不是手工Tag编码,则扩展成员也不能手工编码。
l SET或者CHOICE类型中,扩展部分,要求是按照一定Tag的顺序列出的。Tagging Class的顺序是UNIVERSAL、APPLICATION、PRIVATE和context-specific,每个类中Tag都是升序排列的。
l 当模块定义中使用了EXTENSIBILITY IMPLIED,或者在类型定义结尾只有一个扩展标记,或者在第二个扩展标记前,可以在SEQUENCE、SET和CHOICE类型的扩展插入点,加入一个虚拟成员或者可选项,这样BER、CER或者DER解码器必要时可以通过跟踪具体的异常来确认第一个版本的抽象语法是否能接收未知的扩展成员。这个虚拟成员的Tag和其它ASN.1的Tag不同,为[IMAGINARY 0]。如下:
Person ::= SET
{
surname [0] IA5String,
first-name [1] IA5String,
contact CHOICE
{
phone-number [2] NumericString,
e-mail-address [3] NumericString,
...,
imaginary [IMAGINARY 0] T
},
info CHOICE
{
age [4] INTEGER,
...,
imaginary [IMAGINARY 0] T
}
}
为了避免将来互通问题,对于正在发展的协议,建议在模块定义部分使用EXTENSIBILITY IMPLIED,如:
ModuleName DEFINITIONS AUTOMATIC TAGS
EXTENSIBILITY IMPLIED ::=
BEGIN
-- ...
END