ASN.1编解码的基本使用
最近使用v2x二阶段国标编解码,了解到抽象语义记法ASN.1(Abstract Syntax Notation One) ,学习了一下使用方法,记录一下。
一、ASN.1 基础
ASN.1是一种 ISO/ITU-T 标准,描述了一种对数据进行表示、编码、传输和解码的数据格式。它提供了一整套正规的格式用于描述对象的结构,而不管语言上如何执行及这些数据的具体指代,也不用去管到底是什么样的应用程序,也就是说这种记法独立于编程语言,具有平台无关性。
CertainStructure ::= SEQUENCE {
str VisibleString,
num1 INTEGER,
num2 INTEGER OPTIONAL,
reals SET OF REAL
}
如上所述的ASN.1描述为字符串str,整形,浮点型,用asn1c进行编译,即可生成c语言代码。
typedef struct CertainStructure {
VisibleString_t str;
long num1;
long *num2; /* OPTIONAL */
A_SET_OF(double) reals;
} CertainStructure_t;
asn1c编译器主要是将ASN.1描述编译成c语言代码。具体过程可以见利用开源ASN1C库实现asn.1的编解码(windows) - 咚咚锵锵 - 博客园 (cnblogs.com)
根据上面创建的数据结构完成的使用流程;write_handle
的意思是asn1c编译器的使用提示。
CertainStructure_t *class = 0;
ber_decode(0, &asn_DEF_CertainStructure, &class, buffer, buffer_length);
class->num1 = 123; /* Modify the contents */
der_encode(&asn_DEF_CertainStructure, class, write_handle, 0);
可以通过代码输出xml格式;
xer_fprint(stdout, &asn_DEF_CertainStructure, class);
二、MyTypes
1、MyType 的 ASN.1 定义为:
MyTypes ::= SEQUENCE {
myObjectId OBJECT IDENTIFIER,
mySeqOf SEQUENCE OF MyInt,
myBitString BIT STRING {
muxToken(0),
modemToken(1)
}
}
2、编译的C语言结构定义:
/* Dependencies */
typedef enum myBitString {
myBitString_muxToken = 0,
myBitString_modemToken = 1
} e_myBitString;
/* MyTypes */
typedef struct MyTypes {
OBJECT_IDENTIFIER_t myObjectId;
struct mySeqOf {
A_SEQUENCE_OF(MyInt_t) list;
/* Context for parsing across buffer boundaries */
asn_struct_ctx_t _asn_ctx;
} mySeqOf;
BIT_STRING_t myBitString;
/* Context for parsing across buffer boundaries */
asn_struct_ctx_t _asn_ctx;
} MyTypes_t;
上述数据结构中,_asn_ctx
用来管理,我们在编写过程中不能显式对其进行修改。其他标签,均和ASN.1 格式匹配。
3、编写asn程序;
#include <stdio.h> /* for stdout */
#include <stdlib.h> /* for malloc() */
#include <assert.h> /* for run-time control */
#include "MyTypes.h" /* Include MyTypes definition */
int main() {
/* Define an OBJECT IDENTIFIER value */
int oid[] = { 1, 3, 6, 1, 4, 1, 9363, 1, 5, 0 }; /* or whatever */
/* Declare a pointer to a new instance of MyTypes type */
MyTypes_t *myType;
/* Declare a pointer to a MyInt type */
MyInt_t *myInt;
/* Temporary return value */
int ret;
/* Allocate an instance of MyTypes */
myType = calloc(1, sizeof *myType);
assert(myType); /* Assume infinite memory */
/*
* Fill in myObjectId
*/
ret = OBJECT_IDENTIFIER_set_arcs(&myType->myObjectId,
oid, sizeof(oid[0]), sizeof(oid) / sizeof(oid[0]));
assert(ret == 0);
/*
* Fill in mySeqOf with a couple of integers.
*/
/* Prepare a certain INTEGER */
myInt = calloc(1, sizeof *myInt);
assert(myInt);
*myInt = 123; /* Set integer value */
/* Fill in mySeqOf with the prepared INTEGER */
ret = ASN_SEQUENCE_ADD(&myType->mySeqOf, myInt);
assert(ret == 0);
/* Prepare another integer */
myInt = calloc(1, sizeof *myInt);
assert(myInt);
*myInt = 111222333; /* Set integer value */
/* Append another INTEGER into mySeqOf */
ret = ASN_SEQUENCE_ADD(&myType->mySeqOf, myInt);
assert(ret == 0);
/*
* Fill in myBitString
*/
/* Allocate some space for bitmask */
myType->myBitString.buf = calloc(1, 1);
assert(myType->myBitString.buf);
myType->myBitString.size = 1; /* 1 byte */
/* Set the value of muxToken */
myType->myBitString.buf[0] |= 1 << (7 - myBitString_muxToken);
/* Also set the value of modemToken */
myType->myBitString.buf[0] |= 1 << (7 - myBitString_modemToken);
/* Trim unused bits (optional) */
myType->myBitString.bits_unused = 6;
/*
* Print the resulting structure as XER (XML)
*/
xer_fprint(stdout, &asn_DEF_MyTypes, myType);
return 0;
}
4、最终输出xml文件
<MyTypes>
<myObjectId>1.3.6.1.4.1.9363.1.5.0</myObjectId>
<mySeqOf>
<MyInt>123</MyInt>
<MyInt>111222333</MyInt>
</mySeqOf>
<myBitString>
11
</myBitString>
</MyTypes>
5、从xml中取数据
#include <stdio.h> /* for stdout */
#include <stdlib.h> /* for malloc() */
#include <assert.h> /* for run-time control */
#include "MyTypes.h" /* Include MyTypes definition */
int main(int argc, char *argv[]) {
char buf[1024]; /* Hope, sufficiently large buffer */
MyTypes_t *myType = 0;
asn_dec_rval_t rval;
char *filename;
size_t size;
FILE *f;
/*
* Target variables.
*/
int *oid_array; /* holds myObjectId */
int oid_size;
int *int_array; /* holds mySeqOf */
int int_size;
int muxToken_set; /* holds single bit */
int modemToken_set; /* holds single bit */
/*
* Read in the input file.
*/
assert(argc == 2);
filename = argv[1];
f = fopen(filename, "r");
assert(f);
size = fread(buf, 1, sizeof buf, f);
if(size == 0 || size == sizeof buf) {
fprintf(stderr, "%s: Too large input\n", filename);
exit(1);
}
/*
* Decode the XER buffer.
*/
rval = xer_decode(0, &asn_DEF_MyTypes, &myType, buf, size);
assert(rval.code == RC_OK);
/*
* Convert the OBJECT IDENTIFIER into oid_array/oid_size pair.
*/
/* Figure out the number of arcs inside OBJECT IDENTIFIER */
oid_size = OBJECT_IDENTIFIER_get_arcs(&myType->myObjectId,
0, sizeof(oid_array[0]), 0);
assert(oid_size >= 0);
/* Create the array of arcs and fill it in */
oid_array = malloc(oid_size * sizeof(oid_array[0]));
assert(oid_array);
(void)OBJECT_IDENTIFIER_get_arcs(&myType->myObjectId,
oid_array, sizeof(oid_array[0]), oid_size);
/*
* Convert the sequence of integers into array of integers.
*/
int_size = myType->mySeqOf.list.count;
int_array = malloc(int_size * sizeof(int_array[0]));
assert(int_array);
for(int_size = 0; int_size < myType->mySeqOf.list.count; int_size++)
int_array[int_size] = *myType->mySeqOf.list.array[int_size];
if(myType->myBitString.buf) {
muxToken_set = myType->myBitString.buf[0]
& (1 << (7 - myBitString_muxToken));
modemToken_set = myType->myBitString.buf[0]
& (1 << (7 - myBitString_modemToken));
} else {
muxToken_set = modemToken_set = 0; /* Nothing is set */
}
/*
* Print the resulting structure as XER (XML)
*/
xer_fprint(stdout, &asn_DEF_MyTypes, myType);
return 0;
}
参考链接:ASN.1编解码:asn1c的基本使用_rtoax的博客-CSDN博客_asn1c
三、v2x二阶段VIR消息集合
1、VIR填充问题
typedef struct IARData {
struct PathPlanningPoint *currentPos /* OPTIONAL */;
struct PathPlanning *path_Planning /* OPTIONAL */;
DriveBehavior_t *currentBehavior /* OPTIONAL */;
struct reqs {
A_SEQUENCE_OF(struct DriveRequest) list;
/* Context for parsing across buffer boundaries */
asn_struct_ctx_t _asn_ctx;
} *reqs;
/*
* This type is extensible,
* possible extensions are below.
*/
/* Context for parsing across buffer boundaries */
asn_struct_ctx_t _asn_ctx;
} IARData_t;
在cpp文件中想要给reqs申请地址空间时,如果使用:
iAD->reqs = (struct reqs*)malloc(sizeof(struct reqs));
VsCode会报出不允许使用不完整的类型 的错误。
// cpp 解决方案
iAD->reqs = (struct IARData::reqs*)malloc(sizeof(struct IARData::reqs);
//c 解决方案
iAD->reqs = (struct reqs*)malloc(sizeof(struct reqs));
VIR消息其它字段的填充方式类似于MyStype的填充。
需要注意的问题:
1、对于消息集中可选“optional”的字段,若不填充数据,则将其置为NULL,不然填充失败;若想填充数据,需要先为其申请空间,不然会报段错误。
2、对于消息集中某些字段的数值,需要注意其定义的大小范围,在填充时其值不能超过最大值,不然虽然能够填充成功,但是编码会失败。
3、注意填充数据分辨率。