这几天开始做项目了,涉及到项目传输时的报文设计,在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;
intoutlen = 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 文件将代码粘贴进去,然后在项目中右键导入现有项,将上面下载后得到的几个文件包含到项目目录中即可编译,根据自己的需要把注释的部分解除注释查看效果。