Ubuntu ASN1C实例分析

1 环境准备

实验平台:ubuntu16.04

2 ASN.1介绍

ASN.1是定义抽象数据类型规格形式的标准。是用于描述数据的表示、编码、传输、解码的灵活的记法。它提供了一套正式、无歧义和精确的规则,以描述独立于特定计算机硬件的对象结构。

ASN.1是通信协议中描述数据传输的正式标记(notation),它与语言实现和物理表示无关与应用的复杂度无关。ASN.1特别适合表示现代通信应用中那些复杂的、变化的及可扩展的数据结构。

ASN.1发送任何形式(音频、视频、数据等等)的信息都必须用数字传送。ASN.1只能包含信息的结构方面(没有已经定义的或考虑到的处理数据值的操作)。它不是一个编程语言。 ASN.1格式数据有多种编码方式,包括BER、DER、XER、PER/UPER等

3 ASN.1实例

3.1 ASD1C

ASD1C是ASN.1的c/c++实例。

实例功能:

(1)本工程将ASN文件编译为.c和.h文件,通过调用API实现UPER编码和解码。

(2)编写了一个UDP的client和server端,对数据传输进行了测试。

(3)网上的例程只涉及到BER编码,没有关于UPER编码的历程,本工程采用UPER编码。

3.2 下载ADN1C工具

下载地址:http://lionet.info/asn1c/download.html

3.3 编译ADN1C工具

下载之后进行解压

sudo tar xf asn1c-0.9.28.tar.gz

然后进入解压目录,执行

cd asn1c-0.9.28/

编译安装

参考该目录文件INSTALL.md:


## Compiling From Sources

### Configure

Configure with the default settings:

    test -f configure || autoreconf -iv
    ./configure
    make

Configure with non-standard settings:

asn1c specific ./configure options include:

    --enable-Werror
    --enable-ASN_DEBUG

invoke `./configure --help` for details.

### Build

Build the libraries and the compiler:

    make

Ensure asn1c is still behaving well after compiling on your platform:

    make check

### Install

Install the compiler into a standard location:

    make install
    # Use ./configure --prefix to override install location.

Display the `asn1c` manual page:

    man asn1c

## Quick Usage Guide

For a usage guide and more information please refer to:

 * the [README.md](README.md) file
 * the asn1c manual page `man asn1c`
 * the included quick start PDF [doc/asn1c-quick.pdf](doc/asn1c-quick.pdf)
 * the comprehensive usage documentation [doc/asn1c-usage.pdf](doc/asn1c-usage.pdf)

In case of any difficulties with installing the compiler, consider using
the Online ASN.1 Compiler at http://lionet.info/asn1c.

-- 
Lev Walkin
vlm@lionet.info

最后执行man ans1c,进行验证:

 3.4 创建一个.asn文件

创建一个目录asn_send

sudo mkdir asn_send
cd asn_send/

创建一个.asn文件,以Traffic_light.asn为例

sudo touch Traffic_light.asn
vim Traffic_light.asn

里面按照asn格式填充数据:

MsgTest DEFINITIONS ::=BEGIN
    Msg ::= SEQUENCE{
	length INTEGER,
        latitude INTEGER,         -- latitude of traffic
        longitude INTEGER,        -- longitude of traffic
	heading INTEGER,
	state INTEGER,
	time INTEGER,
	pading INTEGER
    }
END

 3.5 编译.asn文件

利用ans1c工具进行编译,选择PER编码格式

asn1c -gen-PER *.asn

编译之后出现很多.c和.h文件,可以从Msg.h看到我们定义的结构体。

 3.6 编写测试代码

一定要删除asn_send文件里面converter-sample.c文件,自己创建一个send.c文件,自己写了一个对数据的编码和udp发送代码:

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wait.h>
#include <errno.h>
#include <netdb.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/time.h>
#include <stdint.h>


#include <Msg.h>

#define MAXLEN_256 256
  
#define DEST_PORT 8000   
#define DSET_IP_ADDRESS  "127.0.0.1"     //回环测试IP

char sendbuf[64];

/* Msg */
typedef struct _SpatMsg {
	long	 length;
	long	 latitude;
	long	 longitude;
	long	 heading;
	long	 state;
	long	 time;
	long	 pading;
} SpatMsg;


#define dbg_printf(f, a...)                                       \
    do {                                                          \
        fprintf(stdout, "%s(%d): " f, __func__, __LINE__, ## a);  \
    }while (0)

#define ERR_EXIT(m)	\
	do	\
	{	\
		perror(m);	\
		exit(EXIT_FAILURE);	\
	} while(0)

// 编码发送
void Encode_Msg(SpatMsg *spatmsg)
{
    Msg_t *msg;        /* Type to encode */
    asn_enc_rval_t ec; /* Encoder return value */

    /* Allocate the Rectangle_t */
    msg = (Msg_t*)calloc(1, sizeof(Msg_t)); /* not */
    printf("length = %ld\n", sizeof(Msg_t));
    if(!msg) {
        perror("calloc() failed");
        exit(71); /* better, EX_OSERR */
    }

    /* Initialize the Rectangle members , 写死测试*/
    msg->length = 4; /* any random value */
    msg->latitude = 295312345; /* any random value */
    msg->longitude = 1063623456;
    msg->heading = 0x01;
    msg->state = 0x01;
    msg->time = 5;

    // 填充信号灯消息
//    msg->length = spatmsg->length; /* any random value */
//    msg->latitude = spatmsg->latitude; /* any random value */
//    msg->longitude = spatmsg->longitude;
//    msg->heading = spatmsg->heading;
//    msg->state = spatmsg->state;
//    msg->time = spatmsg->time;
//    msg->pading = spatmsg->pading;


    // 对从C结构编码成ASN1格式数据buf
    // UPER
    ec = uper_encode_to_buffer(&asn_DEF_Msg, msg, sendbuf, 64);
    if(ec.encoded  == -1) {
        fprintf(stderr,
            "Could not encode MessageFrame (at %s)\n",
            ec.failed_type ? ec.failed_type->name : "unknown");
        exit(65); /* better, EX_DATAERR */
    } else {
        fprintf(stderr, "Created %s with PER encoded MessageFrame\n",
            "");
    }


    /* Also print the constructed Rectangle XER encoded (XML) */
    xer_fprint(stdout, &asn_DEF_Msg, msg);
	    
}
   
  
 void Socket_send(char *buf){
  /* socket文件描述符 */  
  int sock_fd;  
  
  /* 建立udp socket */  
  sock_fd = socket(AF_INET, SOCK_DGRAM, 0);  
  if(sock_fd < 0)  
  {  
    perror("socket");  
    exit(1);  
  }  
    
  /* 设置address */  
  struct sockaddr_in addr_client;  
  int len;  
  memset(&addr_client, 0, sizeof(addr_client));  
  addr_client.sin_family = AF_INET;  
  addr_client.sin_addr.s_addr = inet_addr(DSET_IP_ADDRESS);  
  addr_client.sin_port = htons(DEST_PORT);  
  len = sizeof(addr_client); 
 
  
   /* send */  
  int send_num;  
    
  send_num = sendto(sock_fd, buf, strlen(buf), 0, (struct sockaddr *)&addr_client, len);  
    
  if(send_num < 0)  
  {  
    perror("sendto error:");  
    exit(1);  
  } 
  
  /* close sock_fd */ 
  close(sock_fd);  
}



int main()
{
	// 填充信号灯消息
	SpatMsg *msg;

	// 编码发送
	Encode_Msg(msg);

	//socket发送
        Socket_send(sendbuf);

    return 0;
}

编译:

gcc *.c -I. -o send.exe

然后在文件夹会出现send.exe执行文件,执行:

./send.exe

执行之后并没什么反应,因为还差一个接收程序。

接着,把asn_send整个文件粘贴复制一份出来,更名asd_rec

进入asd_rec目录,然后删除send.c文件,新建一个rec.c

cd asn_rec/
sudo rm send.c
sudo touch rec.c

自己在rec.c里面写了一个接受udp数据和解码的代码,并把解码后的数据打印出来

#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wait.h>
#include <errno.h>
#include <netdb.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/time.h>
#include <stdint.h>

#include <Msg.h>

#define MAXLEN_256 256
#define SERV_PORT   8000  

char recv_buf[64];

/* Msg */
typedef struct _SpatMsg {
	long	 length;
	long	 latitude;
	long	 longitude;
	long	 heading;
	long	 state;
	long	 time;
	long	 pading;
} SpatMsg;

#define dbg_printf(f, a...)                                       \
    do {                                                          \
        fprintf(stdout, "%s(%d): " f, __func__, __LINE__, ## a);  \
    }while (0)

#define ERR_EXIT(m)	\
	do	\
	{	\
		perror(m);	\
		exit(EXIT_FAILURE);	\
	} while(0)

/*receive*/
char* Socket_rec(){
	/* socket文件描述符 */  
 	int sock_fd;  
  
 	/* 建立udp socket */  
 	sock_fd = socket(AF_INET, SOCK_DGRAM, 0);  
 	if(sock_fd < 0)  {  
   		perror("socket");  
   		exit(1);  
 	}  
    
 	/* 设置address */  
 	struct sockaddr_in addr_serv;  
 	int len;  
 	memset(&addr_serv, 0, sizeof(addr_serv));
	addr_serv.sin_family = AF_INET;  
	addr_serv.sin_port = htons(SERV_PORT); 
	/* INADDR_ANY表示不管是哪个网卡接收到数据,只要目的端口是SERV_PORT,就会被该应用程序接收到 */  
	addr_serv.sin_addr.s_addr = htonl(INADDR_ANY);  //自动获取IP地址
	len = sizeof(addr_serv); 

	/* 绑定socket */  
	if(bind(sock_fd, (struct sockaddr *)&addr_serv, sizeof(addr_serv)) < 0){  
		perror("bind error:");  
		exit(1);  
	}  

        /* receive */ 
	int recv_num;  
	recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_serv, (socklen_t *)&len);  
	if(recv_num < 0)  
	{  
		perror("recvfrom error:");  
		exit(1);  
	}  
	/* close sock_fd */ 
 	close(sock_fd); 
	return recv_buf;

}



// 解析处理
void Decode_Receive(char *recveive_buf)
{
	asn_dec_rval_t rval;
	Msg_t *msg1 = (Msg_t *)calloc(1, sizeof(Msg_t));
	// UPER 解码
	asn_codec_ctx_t *opt_codec_ctx = 0;
	rval = uper_decode(opt_codec_ctx, &asn_DEF_Msg, (void **) &msg1,
			recveive_buf, 256, 0, 0);
	if (rval.code == RC_OK) {
		printf("\n ----- decode ASN success-----\n");
	} else {
		printf("\n ----- decode ASN failed ------\n");
		exit(1);
	}

	// printf the msg
	xer_fprint(stdout, &asn_DEF_Msg, msg1);

	// 执行其他业务
}


int main()
{
	while(1){
		// socket接受并解码
		Decode_Receive(Socket_rec());
	}

	return 0;
}

编译:

gcc *.c -I. -o rec.exe

然后在文件夹会出现rec.exe执行文件

 3.7 代码测试

打开两个终端,分别进入asn_send和asn_rec目录,执行./send.exe和./rec.exe

  • 18
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 34
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值