通信架构
客户机和服务器在开始通信前,通信信道必须先完成预连接。预连接建立后,默认具有一个最低权限的应用连接,客户机和服务器之间可直接进行数据交换。当客户机需要得到更高权限的服务器服务时,客户机必须发起建立更高权限的应用连接。
- 建立应用连接;
- 数据交换;
- 断开应用连接。
客户机和服务器之间的信息交换借助于通信协议实现。客户机和服务器的应用进程使用协议应用层的服务,应用层是唯一包含服务组件的协议层,应用层数据单元(APDU)通过数据链路层协议传输帧的链路用户数据域传输。
帧格式
链路层帧的基本单元为8位字节,传输顺序为低位在前,高位在后;低字节在前,高字节在后。
起始字符68H | 长度域L | 控制域C | 地址域A | 帧头校验HCS | 链路应用数据 | 帧校验FCS | 结束字符16H |
---|---|---|---|---|---|---|---|
1Byte | 2Byte | 1Byte | (N+1)Byte | 2Byte | N Byte | 2Byte | 1Byte |
长度域L
bit15 | bit14 | bit13~bit0 |
---|---|---|
保留 | 帧数据长度单位 | 帧数据长度值 |
- bit0…bit13:帧数据长度值,是传输帧中不包含起始字符和结束字符的数据长度;
- bit14:帧数据长度单位,0 表示帧数据长度单位为字节,1 表示帧数据长度单位为千字节;
- bit15:保留。
控制域C
bit7 | bit6 | bit4 | bit3 | bit2~bit0 |
---|---|---|---|---|
传输方向DIR | 启动标志 PRM | 分帧标志 | 保留 | 扰码标志 SC |
传输方向及启动标志位
传输方向位及启动标志位定义:
- 传输方向位:bit7=0 表示此帧是由客户机发出的;bit7=1 表示此帧是由服务器发出的;
- 启动标志位:bit6=0 表示此帧是由服务器发起的;bit6=1 表示此帧是由客户机发起的。
DIR | PRM | 组合意义 |
---|---|---|
0 | 0 | 客户机对服务器上报的响应 |
0 | 1 | 客户机发起的请求 |
1 | 0 | 服务器发起的上报 |
1 | 1 | 服务器对客户机请求的响应 |
分帧标志位
分帧标志位: bit5=0,表示此帧链路用户数据为完整APDU(应用层数据单元);bit5=1,表示此帧链路用户数据为APDU片段。
扰码标志位
扰码标志位: bit3=0,表示此帧链路用户数据不加扰码;bit3=1,表示此帧链路用户数据加扰码,发送时链路用户数据按字节加33H。
功能码
功能码 | 服务类型 | 应用说明 |
---|---|---|
0 | 保留 | |
1 | 链路管理 | 链路连接管理(登录,心跳,退出登录) |
2 | 保留 | |
3 | 用户数据 | 应用连接管理及数据交换服务 |
4~7 | 保留 |
地址域A
地址域A由可变字节数的服务器地址SA和1字节的客户机地址CA组成。
服务器地址 SA
服务器地址SA由1字节地址特征和N个字节地址组成
bit7-bit6 | bit5-bit4 | bit3-bit0 |
---|---|---|
地址类型 | 逻辑地址 | 地址长度 |
扩展逻辑地址/分路地址 | ||
地址 |
地址特征定义
- bit0…bit3:为地址的字节数,取值范围:0…15,对应表示 1…16 个字节长度;
- bit4…bit5:逻辑地址;
- bit5=0 表示无扩展逻辑地址,bit4 取值 0 和 1 分别表示逻辑地址 0 和 1;
- bit5=1 表示有扩展逻辑地址,bit4 备用;地址长度 N 包含 1 个字节的扩展逻辑地址,取值范围 2…255,表示逻辑地址 2…255;
- bit6…bit7:为服务器地址的地址类型,0 表示单地址,1 表示通配地址,2 表示组地址,3表示广播地址。
扩展逻辑地址和地址要求如下:
- 扩展逻辑地址取值范围 2…255;
- 编码方式为压缩 BCD 码,0 保留;
- 当服务器地址的十进制位数为奇数时,最后字节的 bit3…bit0 用 FH 表示。
单地址
单地址的长度为可变字节
组地址
组地址的长度为可变字节。组地址对系统中属于该群组的服务器有效,无需应答。
通配地址
通配地址的长度为可变字节。每字节二进制高低各4位分别编码表示两个0到9的十进制数或通配符AH。通配符按十进制位使用,对应的十进制位为AH时,表示该十进制位可为0到9的任意值。
广播地址
广播地址的长度固定为1字节,广播地址=AAH。广播地址对系统所有服务器有效,无需应答。
客户机地址 CA
客户机地址CA用1字节表示,0表示不关注客户机地址。
帧头校验 HCS/ 帧校验 FCS
帧头校验HCS为2字节,是对帧头部分不包含起始字符和HCS本身的所有字节的校验
/*
* u16 represents an unsigned 16-bit number. Adjust the typedef for
* your hardware.
* Drew D. Perkins at Carnegie Mellon University.
* Code liberally borrowed from Mohsen Banan and D. Hugh Redelmeier.
*/
typedef unsigned short u16;
/*
* FCS lookup table as calculated by the table generator.
*/
static u16 fcstab[256] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78};
#define PPPINITFCS16 0xffff /* Initial FCS value */
#define PPPGOODFCS16 0xf0b8 /* Good final FCS value */
/*
* Calculate a new fcs given the current fcs and the new data.
*/
u16 pppfcs16(fcs, cp, len)
register u16 fcs;
register unsigned char *cp;
register int len;
{
ASSERT(sizeof(u16) == 2);
ASSERT(((u16)-1) > 0);
while (len--)
fcs = (fcs >> 8) ^ fcstab[(fcs ^ *cp++) & 0xff];
return (fcs);
}
/*
* How to use the fcs
*/
tryfcs16(cp, len) register unsigned char *cp;
register int len;
{
u16 trialfcs;
/* add on output */
trialfcs = pppfcs16(PPPINITFCS16, cp, len);
trialfcs ^= 0xffff; /* complement */
cp[len] = (trialfcs & 0x00ff); /* least significant byte first */
cp[len + 1] = ((trialfcs >> 8) & 0x00ff);
/* check on input */
trialfcs = pppfcs16(PPPINITFCS16, cp, len + 2);
if (trialfcs == PPPGOODFCS16)
printf("Good FCS\n");
}
/*
* Generate a FCS-16 table.
* Drew D. Perkins at Carnegie Mellon University.
* Code liberally borrowed from Mohsen Banan and D. Hugh Redelmeier.
* The FCS-16 generator polynomial: x**0 + x**5 + x**12 + x**16.
*/
#define P 0x8408
/*
* NOTE The hex to "least significant bit" binary always causes
* confusion, but it is used in all HDLC documents. Example: 03H
* translates to 1100 0000B. The above defined polynomial value
* (0x8408) is required by the algorithm to produce the results
* corresponding to the given generator polynomial
* (x**0 + x**5 + x**12 + x**16)
*/
main()
{
register unsigned int b, v;
register int i;
printf("typedef unsigned short u16;\n");
printf("static u16 fcstab[256]={");
for (b = 0;;)
{
if (b % 8 == 0)
printf("\n");
v = b;
for (i = 8; i--;)
v = v & 1 ? (v >> 1) ^ P : v >> 1;
printf("\t0x%04x", v & 0xFFFF);
if (++b == 256)
break;
printf(",");
}
printf("\n};\n");
}
传输规则
字节规则
采用串行通信方式发送数据时:
- 线路空闲状态为二进制 1;
- 在有效数据帧前加 4 个 FEH 作为前导码;
- 数据链路层帧的字节之间无线路空闲间隔;两帧之间的线路空闲间隔至少 33 位。
分帧传输
分帧传输规则
当一个APDU数据组帧的长度超过协商的最大帧长度时,采用分帧传输。采用分帧传输时,控制域中分帧标志位置1。数据接收端应对分帧进行逐条确认。
分帧传输格式定义
分帧传输时,数据链路层的链路用户数据为分帧传输帧,分为数据帧和确认帧;分帧传输的数据帧包含分帧格式域和APDU片段。分帧传输的确认帧仅包含分帧格式域,不含APDU片段
bit15-bit14 | bit13-bit12 | bit11-bit0 |
---|---|---|
分帧类型 | 保留 | 表示分帧传输的帧序号,取值范围 0…4095,循环使用 |
分帧类型定义:
- 表示分帧传输起始帧;
- 表示分帧传输末尾帧;
- 表示分帧传输确认帧;
- 表示分帧传输中间帧
分帧传输交互规则
分帧传输交互可由服务器或客户机任意一侧发起,适用于主动发起的数据服务或者被动应答的数据服务,发送方将APDU应用数据单元分割成若干个片段,通过分帧方式依次发送。
分帧过程如下:
- 分帧的第一帧数据,帧序号(block)=0,分帧类型=0;
- 分帧传输确认,分帧类型=2,block 值为最近一次收到正确的帧序号;
- 分帧的发送方在接收到确认帧后传输下一个数据帧,分帧类型=3,分帧序号 block=接收到确认帧的帧序号+1,重复 2 和 3;
- 分帧的最后一帧,block=最后一帧的帧序号,分帧类型=1,发送方在接收到最后一个确认帧后,分帧传输过程完成。
应用层
应用层服务对象是构成客户机和服务器应用层的主要组件,它使用的数据链路层提供的服务,服务规范包含客户机和服务器应用进程在各自应用层的逻辑接口,并向应用进程提供服务。客户机和服务器的应用服务对象都包括预连接、应用连接和数据交换三个必备组件。
应用层数据单元(APDU)的标记规则遵循ASN.1的抽象语法,详见GB/T 16262.1—2006。应用层数据单元(APDU)的编码规则遵循A-XDR,详见DL/T 790.6—2010。应用层紧凑服务的数据单元(APDU)的编码规则遵循PER,详见GB/T 16263.2-2006。
编码结构
A-XDR规定了一种编码规则,可用于对定义为单一ASN.1类型(最外层的类型)的抽象语法数值 进行编码或解码。这种单一ASN.1类型可以是简单类型也可以是复合类型。复合类型的数据项可以是简 单类型或复合类型。
A-XDR 编码规则利用了 DLMS PDU 发送端和接收端都用相同的抽象语法规范运行这一事实。用 BER 时,抽象语法的任何值的编码结构都是TLV(type-length-value,类型一长度一数值)形式。A-XDR 只在必要时才对数据的类型和长度编码,如不知道编码值的类型,就无法确定编码的结构。
为使A-XDR尽量简单,进行抽象语法编码时,应用以下限制条件:
- 不支持DLMS以外的ASN.1类型数据的编码”;
- ASN.1选择(CHOICE)类型数据只用于有显式标记4’的数据项。
A-XDR是面向字节的编码规则。这意味着每一部分乃至全部的编码的字节数是整数。
BER编码(见ITU-T的X.209)的基本结构:类型、长度和内容,这三部分在BER中可表示为标识符I(Identifier)、长度L(Length)和内容C(Contents),如图所示。标识符部分用于标识类型,长度部分用于查找内容区的末端,内容部分用于传送这种类型的可能值。内容域可简化为一系列字节6’(原始编码)或一系列嵌套编码(构造编码),
嵌套的深度按需要决定,并以原始编码或空内容的构造编码结束。
A-XDR的编码结构与BER基本相同,但为了利用DLMS PDU发送端和接收端按同样的抽象语法规范运行这一事实,当标识符和长度域传送冗余的信息时,A-XDR就不对它们编码(不对其中一个域或两个域编码不会导致编码不可解释或含糊不清)。因此,A-XDR编码的结构如图所示。
A-XDR编码规则规定:
- 用于内容域的编码规则;
- 如有长度域,对长度域编码;
- 如有标识域,对标识域编码。
例:
Value ::= SEQUENCE {
A integer16,
B unsigned16
}
integer16 ::= INTEGER(-32768~32768)
unsigned16 ::= INTEGER(0~65535)
假设对A和B编码,其值分别为0x1234和0x5678,上述序列的BER编码如下:
同一序列的A-XDR编码如下:
编码规则
对任何ASN.1类型的数据进行A-XDR编码,其字节数都是整数,每个字节有8位。这些字节从最 外层的ASN.1类型的标识符域编码的第一个字节开始,可认为这个字节为最高位。DL/T 790的本部分 做以下规定:
- 不系统地对A-XDR编码的字节编号,但有时,为便于理解可加说明(例如,值的第1字节等)。
- 每个字节的位的编号为1~8,其中第8位是最高位。
标识符域
标识符域的作用是确定编码值的类型。假如信息的发送端和接收端都以相同抽象句法规范运行,只在以下情况下标识符域才传输信息:
- 应从不同CHOICE类型中选择一种数据类型;
- 应指出序列(SEQUENCE)中有可选(OPTIONAL)项存在;
- 应指出序列(SEQUENCE)中有默认(DEFAULT)项存在。
A-XDR只在上述情况时有标识符域。此外,当ASN.1规范(ASN.1的显式标记)要求对标识符域进行编码时,A-XDR对标识符编码。
在 1 情况下,A-XDR要求选择(CHOICE)的所有备选在ASN.1中定义为显式标记类型。这时,编码标识形成了标识符域。
在 2 和 3 情况下,可选(OPTIONAL)和默认(DEFAULT)项是否存在可由布尔(BOOLEAN) 类型的存在标记表示。这些可选项值的标识符域就是存在标记值的A-XDR编码。
另一方面,当ASN.1定义中包含ASN.1的显式标记时,A-XDR就必须对标识符域编码。
这些类型的 A-XDR 编码定义和它们的BER编码相同。这样做的目的是使对长度编码,便于省略一些结构。
这些类型的标识符域是ASN.1标记的编码值,所占字节数是整数,至少为1,如ITU-T的X.209规定。
LD/T698.45 接口类及对象实例使用的数据类型定义如下:
类型描述 | 标记 | 定义 | 数值范围 |
NULL | 0 | 空 | |
array | 1 | SEQUENCE OF Data(见7.3.1 ) 数组的元素在对象属性或方法的描述中定义 | |
structure | 2 | SEQUENCE OF Data(见7.3.1 ) 结构的元素在对象属性或方法的描述中定义 | |
bool | 3 | 布尔值 | 1 或 0 |
bit-string | 4 | 位串 | |
double-long | 5 | 32位整数 | -2³...2³¹-1 |
double-long-unsigned | 6 | 32位正整数 | 0...2³²-1 |
保留 | 7-8 | ||
octet-string | 9 | 8位字节串 | |
visible-string | 10 | ASCII字符串 | |
保留 | 11 | ||
UTF8-string | 12 | UTF-8编码的字符串 | |
保留 | 13-14 | ||
integer | 15 | 8位整数 | -128...127 |
long | 16 | 16位整数 | -32768...32767 |
unsigned | 17 | 8位正整数 | 0...255 |
long-unsigned | 1O 10 | 16位正整数 | 0...65535 |
保留 | 19 | ||
long64 | 20 | 64位整数 | -26...2⁶³-1 |
long64-unsigned | 21 | 64位正整数 | 0...2⁶⁴-1 |
enum | 22 | 枚举的元素在对象属性或方法的描述中定义 | 0...255 |
float32 | 23 | 32位浮点数 | |
float64 | 24 | 64位浮点数 | |
date_time | 25 | octet-string(SIZE(10)) | |
date | 26 | octet-string(SIZE(5)) | |
time | 27 | octet-string(SIZE(3)) | |
date_time_s | 28 | octet-string(SIZE(7)) | |
保留 | 29-79 | ||
OI | 80 | 见0 | |
0AD | 81 | 见0 | |
ROAD | 82 | 见0 | |
OMD | 83 | 见0 | |
TI | 84 | 见0 | |
TSA | 85 | 见0 | |
MAC | 86 | 见0 | |
RN | 87 | 见0 | |
Region | 88 | 见0 | |
Scaler_Unit | 89 | 见0 | |
RSD | 90 | 见0 | |
CSD | 91 | 见0 | |
MS | 92 | 见0 | |
SID | 93 | 见0 | |
SID_MAC | 94 | 见0 | |
COMDCB | 95 | 见0 | |
RCSD | 96 | 见0 | |
VQDS | 97 | 见0 | |
保留 | 98-255 | ||
注:表中的“位”表示1个二进制信息单位(bit)。 | |||
长度域
在A-XDR中长度域(如存在)位于内容域的前面。它明确地表示内容区的长度,所占字节数为整数。如信息的发送端和接收端都以相同的抽象句法规范运行,只在对可变长度的ASN.1类型编码时长度域才传输信息。可能的情况如下:
- 长度可变的整数(INTEGER);
- 长度可变的位串(BIT STRING);
- 长度可变的字节串(BYTE STRING);
- 长度可变的类型序列(SEQUENCE OF)。
只在以上情况以及ASN.1规范(ASN.1显式标记)要求时,A-XDR才对长度域编码。
在上述四种情况下,长度域编码为一个可变长度的整数。其他情况,除只有确定形 式可以[见脚注7]使用的限制外,A-XDR采用与BER定义相同的方法(见ITU-T的X.209)。不允许 A-XDR的长度域用不确定型。
内容域
内容域是编码的本体。它传输实际值,由零或多个字节组成,总体来讲,组包和分析报文的过程其实也是查表的过程,没什么技术含量。
例1:登录
我们开始分析应用层的数据单元部分。
01H代表连接请求;81H代表连接应答。
PIID-ACD:带请求访问标识的序号及优先标志(Priority and Invoke ID with ACD)
表6内容:
PIID 用于客户机 APDU(Client-APDU)的服务数据类型中,其中:
- bit7(服务优先级)——0:普通优先级,1:高优先级,在应答 APDU 中,其值与请求的 APDU 相同。
- bit6(保留)。
- bit0…bit5(服务序号)——二进制编码表示 0…63,在应答 APDU 中,其值与请求的 APDU 相同。
综上所述,00表示普通优先级,服务序号为0。
请求类型为登录(0)。
心跳周期为180秒。
请求时间为date_time,其数据类型定义如下:
报文中,07E0H表示2016;05H表示5月,其余如此类推。
例2:设置一个对象属性请求
以上报文是设置时钟的请求。
06H是指设置请求。
01H表示请求设置一个对象属性。
SetRequestNormal 由 3 部分组成: 服务序号-优先级(PIID)、对象属性描述符(OAD)、数据。
40H 00H 02H 00H, 对象标识数据类型为 long-unsigned, 即 4000H;属性标识及其特征为02H,即表示在0值特征情况下的对象的所有属性;休息内元素索引为00,表示整个属性的全部内容。
综上所述,02H表示普通优先级,服务序号为2;设置整个时钟对象的全部属性内容。
ICH 表示当前写入的数据类型是 date_time_s ;且没有时间标签域。