国标二阶段VIR消息集调试总结
最近项目需要,开始着手VIR消息集合的填充、收发调试,遇到几个坑,记录一下。
一、msgFrameExt帧中的messageId
仔细看国标、仔细看国标、仔细看国标
标准中有明确告知该字段对应填充的值,若随意填充回导致接收端decode时出现错误。
ExtMsgID ::= INTEGER (0..32767)
********** Test Message ************************
testData ExtMsgID ::= 0
********** DAY II Messages *********************
rtcmData ExtMsgID ::= 10
rscData ExtMsgID ::= 11
ssmData ExtMsgID ::= 12
virData ExtMsgID ::= 13
pamData ExtMsgID ::= 14
psmData ExtMsgID ::= 15
clpmmData ExtMsgID ::= 16
vpmData ExtMsgID ::= 17
二、uper_encode函数
使用uper_encode函数进行的uper编码,uper_encode源码如下:
asn_enc_rval_t uper_encode(const asn_TYPE_descriptor_t *td, const asn_per_constraints_t *constraints, const void *sptr, asn_app_consume_bytes_f *cb, void *app_key) {
asn_per_outp_t po;
asn_enc_rval_t er;
/*
\* Invoke type-specific encoder.
*/
if(!td || !td->op->uper_encoder)
ASN__ENCODE_FAILED; /* PER is not compiled in */
po.buffer = po.tmpspace;
po.nboff = 0;
po.nbits = 8 * sizeof(po.tmpspace);
po.output = cb ? cb : ignore_output;
po.op_key = app_key;
po.flushed_bytes = 0;
er = td->op->uper_encoder(td, constraints, sptr, &po);
if(er.encoded != -1) {
size_t bits_to_flush;
bits_to_flush = ((po.buffer - po.tmpspace) << 3) + po.nboff;
/* Set number of bits encoded to a firm value */
er.encoded = (po.flushed_bytes << 3) + bits_to_flush;
if(_uper_encode_flush_outp(&po)) ASN__ENCODE_FAILED;
}
return er;
}
需要留意的是返回值asn_enc_rval_t和回调函数asn_app_consume_bytes_f *cb;
/*
* Type of the return value of the encoding functions (der_encode, xer_encode).
*/
typedef struct asn_enc_rval_s {
/*
* Number of bytes encoded.
* -1 indicates failure to encode the structure.
* In this case, the members below this one are meaningful.
*/
ssize_t encoded;
/*
* Members meaningful when (encoded == -1), for post mortem analysis.
*/
/* Type which cannot be encoded */
const struct asn_TYPE_descriptor_s *failed_type;
/* Pointer to the structure of that type */
const void *structure_ptr;
} asn_enc_rval_t;
该结构体包含3个成员【编码的字节数、无法编码的类型描述指针、指向该类型结构的指针】。asn1c库编码函数返回的encoded,存在一个小问题 - 渤海深处的博客 (rustintherubble.github.io) 由该描述可知,直接通过encode函数获取编码长度需确认返回值编码长度的含义,这里一般不建议使用ec.encoded作为编码输出的最后长度,建议使用per_encoder.c里面的encode_to_buffer_cb这个函数。
static int encode_to_buffer_cb(const void *buffer, size_t size, void *key) {
enc_to_buf_arg *arg = (enc_to_buf_arg *)key;
if(arg->left < size)
return -1; /* Data exceeds the available buffer size */
memcpy(arg->buffer, buffer, size);
arg->buffer = ((char *)arg->buffer) + size;
arg->left -= size;
printf("encode_to_buffer_cb:\n");
for (int i = 0; i < size; i++)
{
printf("%02x", *((unsigned char *)buffer + i));
}
printf("\n");
return 0;
}
三、关于ASN_FREE_STRUCT
在使用ASN_FREE_STRUCT(&asn_DEF_MessageFrame, msg)的时候,仅仅释放了msg还不够,通过单步调试看到,即使free了msg,但是在下一次为msg申请空间后,msg所在地址空间内居然保留了上一次解码所产生的数值,这样的话,如果每次解码的消息体结构是一样的还好说,如果消息体结构不一致,也会报段错误,而且也会导致数据重复,最保险的做法是,在为msg申请空间后,将其地址空间内的值都置为0,即memset(mf, 0, sizeof(MessageFrame_t));当然了,其实不管在哪,自己申请了一块地址空间后,最保险还是将其内的数值重置为0。关于再次申请结构不一致,导致段错误的问题,暂时不是知道怎么解决,现有的解决思路是在free前把msg中的值都置0,但这种思路暂时没有验证过。
四、关于xer_fprint
在调试过程中多使用xer_fprint函数打印填充的消息集合,建议在encode之前和decode之后都进行一次打印,很容易找到问题。若encode之前xer_fprint打印有问题,那么encode肯定会失败,而且问题一般出在xer_fprint打印中断的地方,多注意这个地方填充的数据;decode之后的xer_fprint打印也同理。