ASN.1 编码基础数据类型

这几天开始做项目了,涉及到项目传输时的报文设计,在C/S架构的项目设计中,传递数据一定要有一定的格式,这样服务端和客户端才能区分开来。除了格式以外还要考虑到传递的数据如果是指针怎么办?如果是NULL怎么办?等等问题,这些问题其实有很多中解决方案,本文就介绍一种 ASN.1 编码格式,当然本文没办法大篇幅的介绍 ASN.1 编码的格式、好处等等内容,网络上的资料有很多,本文主要是记录代码上如何实现对基础数据类型的编码,以备以后忘记了具体细节时回来查看。


相关头文件下载

点击下载:itcast_asn1_der 包含.h和.cpp

代码实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "itcast_asn1_der.h"

typedef struct _tag_teacher
{
	int age;
	char name[20];
	char* p;
	char plen;
}Teacher;

int mywritefile(unsigned char *buf, int len)
{
	FILE *fp = NULL;

#ifdef WIN32
	fopen_s(&fp, "c:/teacher.ber", "wb+");
#else
	fp = fopen("c:/teacher.ber", "wb+");
#endif

	if (fp == NULL)
	{
		printf("fopen file error \n");
		return -1;
	}
	fwrite(buf, 1, len, fp);
	fclose(fp);
	return 0;
}

/*
	函数功能:编码 Teacher 结构体,通过
	参数介绍:
		Teacher *pStruct	:Teacher 类型的结构体指针
		unsigned char** out	:unsigned char 类型的空指针,用来接收内部处理完成后返回的数据
		int* outlen			:int 类型的指针,用来接收内部处理完成后数据的长度
*/

int EncodeStruct(Teacher *pStruct, unsigned char** out, int* outlen)
{
	int ret = 0;						// 记录返回值
	ITCAST_ANYBUF *pHeadBuf = NULL;		// 头节点
	ITCAST_ANYBUF *pCur = NULL;		// 游标,储存新编码的节点域并与头节点连接
	ITCAST_ANYBUF *pTeacher = NULL;		// 临时节点用来接收整条链表编码后的结果

	if (out == NULL || outlen == NULL)
	{
		return -1;
	}

	// 如果传递的第一个参数是NULL,那么正常也对 NULL 进行编码
	if (pStruct == NULL)
	{
		DER_ItAsn1_WriteNull(&pTeacher);
		goto END;
	}

	// 编码第一个 int 域保存到第一个节点 pHeadBuf 中
	ret = DER_ItAsn1_WriteInteger(pStruct->age, &pHeadBuf);
	if (ret != 0)
	{
		printf("func DER_ItAsn1_WriteInteger() err:%d\n", ret);
		return ret;
	}

	// 让游标指向头节点
	pCur = pHeadBuf;

	// 让头节点的下一个节点(pCur->next)接收编码name的数据
	ret = EncodeChar(pStruct->name, strlen(pStruct->name), &pCur->next);
	if (ret != 0)
	{
		DER_ITCAST_FreeQueue(pHeadBuf);
		printf("func EncodeChar() err:%d\n", ret);
		return ret;
	}

	// 连接链表,向前递进,编码指针域
	pCur = pCur->next;
	ret = EncodeChar(pStruct->p, pStruct->plen, &pCur->next);
	if (ret != 0)
	{
		DER_ITCAST_FreeQueue(pHeadBuf);
		printf("func EncodeChar() err:%d\n", ret);
		return ret;
	}

	// 连接链表,向前递进,编码 plen
	pCur = pCur->next;
	ret = DER_ItAsn1_WriteInteger(pStruct->plen, &pCur->next);
	if (ret != 0)
	{
		DER_ITCAST_FreeQueue(pHeadBuf);
		printf("func DER_ItAsn1_WriteInteger() err:%d\n", ret);
		return ret;
	}

	// 将整个链表一起编码
	ret = DER_ItAsn1_WriteSequence(pHeadBuf, &pTeacher);
	if (ret != 0)
	{
		DER_ITCAST_FreeQueue(pHeadBuf);
		printf("func DER_ItAsn1_WriteSequence() err:%d\n", ret);
		return ret;
	}

END:
	// 将数据传出
	*out = ( unsigned char* ) malloc( pTeacher->dataLen );

#ifdef WIN32
	memcpy_s(*out, pTeacher->dataLen, pTeacher->pData, pTeacher->dataLen);
#else
	memcpy(*out, pTeacher->pData, pTeacher->dataLen);
#endif // WIN32

	*outlen = pTeacher->dataLen;
	// 释放内存
	DER_ITCAST_FreeQueue(pHeadBuf);
	DER_ITCAST_FreeQueue(pTeacher);

	return ret;
}

int TeacherDecode(unsigned char* out, int outlen, Teacher** pTeacher)
{
	int ret = 0;
	return ret;
}

int main(int argc, char* argv[])
{
	int ret = 0;

	Teacher teacher;
	teacher.age = 25;

#ifdef WIN32
	strcpy_s(teacher.name, sizeof(teacher.name), "myCode");
#else
	strcpy(teacher.name, "myCode");
#endif // WIN32

	teacher.p = (char*)malloc(100);
	
#ifdef WIN32
	strcpy_s(teacher.p, 100, "pStructTest");
#else
	strcpy(teacher.p, "pStructTest");
#endif // WIN32

	teacher.plen = strlen(teacher.p);
	
	// 接收数据的变量
	unsigned char*	out = NULL;
	int				outlen = 0;

	// 编码结构体
	ret = EncodeStruct(&teacher, &out, &outlen);
	if (ret != 0)
	{
		free(teacher.p);
		printf("func EncodeStruct() err:%d\n", ret);
		return ret;
	}

	// 将编码后的数据写入到文件中,可通过 ber 查看工具进行查看
	mywritefile(out, outlen);

	// 解码
	Teacher *pTeacher = NULL;
	ret = TeacherDecode(out, outlen, &pTeacher);

	if (teacher.age == pTeacher->age &&
		strcmp(teacher.name, pTeacher->name) &&
		strcmp(teacher.p, pTeacher->p) &&
		teacher.plen == pTeacher->plen)
	{
		printf("一样\n");
	}
	else 
	{
		printf("不一样\n");
	}

	// 释放内存
	free(out);
	free(teacher.p);

	return 0;
}

 

代码编译

在 VS 中新建一个 win32 控制台空项目,新建 mian.c 文件将代码粘贴进去,然后在项目中右键导入现有项,将上面下载后得到的几个文件包含到项目目录中即可编译,根据自己的需要把注释的部分解除注释查看效果。

 

评论