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