安全传输平台项目——统一报文-动态库制作-统一通信组件-函数接口
在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
10-安全传输平台项目-第03天(统一报文-动态库制作-统一通信组件-函数接口)
目录:
一、复习
1、ANSI.1抽象语法标记
2、教师结构体的编码解码
二、学习目标
三、安全传输平台项目—统一报文编码组和统一通信组件
1、教师结构体编码解码封装
2、wind下制作动态库原理
3、wind动态库制作
4、库使用问题总结
5、Linux动态库制作理论
6、Linux下动态库制作
7、Makefile复习
8、Makefile项目目录管理
四、安全传输平台项目—统一通信组件
1、统一通信组件-客户端服务器API
2、统一通信组件-服务器实现
一、复习
1、ANSI.1抽象语法标记
2、教师结构体的编码解码
int MsgEncode(void *teacher, unsigned char **out, int *outlen, int type)
{
1.编码type
2.编码Teacher结构体
1)name
2)age
3)p
4)plen
5)Teacher结构体本身 --> Anybuf
3.编码 大结构体(type、Teacher)
}
二、学习目标
1 MsgKey_Req 结构体 编码解码 三个 20
2 MsgKey_Req三个api函数 集成到 框架中 40
3 生成win mymsgreal.dll 50
4 win 编程测试案例 60
5 win移植到到linux 70
6 编程linux动态库 80
7 测试和调试linux动态库 90
8 代码优化、makefile 100
三、安全传输平台项目—统一报文编码组和统一通信组件
重点:深入理解,报文编码解码组件和业务流模块的解耦合
》统一报文编码解码设计思想
统一报文编解码组件:实现了把各种各样的数据类型进行隐藏、把各种各样的报文结果类型进行隐藏
》统一报文编码解码设计思想
1)定义统一报文API 打桩API函数 (keymng_msg.c keymng_msg.h)
2)编写统一报文组件的测试案例
3)编码实现统一报文组件的编码业务流
4)编码实现统一报文组件的解码业务流程)
5)优化统一报文组件 日志/内存泄漏)
6)统一报文组件动态库 和 动态库测试程序
7)统一报文组件 linux下的移植 跨平台的移植
Win系统文件上传/linux系统文件编译/动态库工程makefile和动态库文件
1、教师结构体编码解码封装
keymng_msg.h
#ifndef _KEYMNG_MSG_H_ #define _KEYMNG_MSG_H_ #ifdef __cplusplus extern "C" { #endif #define KeyMng_ParamErr 200 //输入参数失败 #define KeyMng_TypeErr 201 //输入类型失败 #define KeyMng_MallocErr 202 //分配内存失败 #define KeyMng_NEWorUPDATE 1 //1 密钥协商 #define KeyMng_Check 2 //2 密钥校验 #define KeyMng_Revoke 3 //3 密钥注销 #define ID_MsgKey_Teacher 80 typedef struct _Teacher { char name[64]; int age; char *p; int plen; }Teacher; //密钥请求报文 --- 结构体 #define ID_MsgKey_Req 60 typedef struct _MsgKey_Req { //1 密钥协商 //2 密钥校验; //3 密钥注销 int cmdType; //报文命令码 char clientId[12]; //客户端编号 char AuthCode[16]; //认证码 char serverId[12]; //服务器端I编号 char r1[64]; //客户端随机数 }MsgKey_Req; //密钥应答报文 --- 结构体 #define ID_MsgKey_Res 61 typedef struct _MsgKey_Res { int rv; //返回值 char clientId[12]; //客户端编号 char serverId[12]; //服务器编号 unsigned char r2[64]; //服务器端随机数 int seckeyid; //对称密钥编号 //modfy 2015.07.20 }MsgKey_Res; /* pstruct : 输入的报文数据 ; (指向相应结构体的指针) type : 输入的类型标识(函数内部通过type 得到 pstruct 所指向的报文类型) poutData: 输出的编码后的报文 ; outlen : 输出的数据长度; */ int MsgEncode( void *pStruct , /*in*/ int type, unsigned char **outData, /*out*/ int *outLen ); /* inData : 输入的编码后的数据; inLen : 输入的数据长度 ; pstruct : 输出的解码后的数据; (其空间是在内部开辟的,也需要用内部定义的free函数进行释放) type : 结构的类型标识(返回类型标识,使得调用者通过flag进行判断,将pstruct 转换为相应的结构) */ int MsgDecode( unsigned char *inData,/*in*/ int inLen, void **pStruct /*out*/, int *type /*out*/); /* 释放 MsgEncode( )函数中的outData; 方法:MsgMemFree((void **)outData, 0); 释放MsgDecode( )函数中的pstruct结构体,MsgMemFree((void **)outData, type); type : 输入参数,便于函数判断调用哪个结构体的free函数 */ int MsgMemFree(void **point,int type); #ifdef __cplusplus } #endif #endif
keymng_msg.c
#include <stdarg.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include "keymng_msg.h" #include "itcast_asn1_der.h" int TeacherEncode_(Teacher *pTeacher, ITCAST_ANYBUF **outData) { int ret = 0; ITCAST_ANYBUF *pTmp = NULL, *pHeadBuf = NULL; ITCAST_ANYBUF *pTmpBuf = NULL; ITCAST_ANYBUF *pOutData = NULL; unsigned char *tmpout = NULL; int tmpoutlen = 0; // 把C语言的buf 转化成 ITCAST_ANYBUF ret = DER_ITCAST_String_To_AnyBuf(&pTmpBuf, pTeacher->name, strlen(pTeacher->name)); if (ret != 0) { printf("func DER_ITCAST_String_To_AnyBuf() err:%d \n", ret); return ret; } //编码 name ret = DER_ItAsn1_WritePrintableString(pTmpBuf, &pHeadBuf); if (ret != 0) { DER_ITCAST_FreeQueue(pTmpBuf); printf("func DER_ITCAST_String_To_AnyBuf() err:%d \n", ret); return ret; } //两个辅助指针变量 指向 同一个节点 pTmp = pHeadBuf; //编码 age ret = DER_ItAsn1_WriteInteger(pTeacher->age, &(pTmp->next)); if (ret != 0) { DER_ITCAST_FreeQueue(pHeadBuf); //释放链表 以免内存泄漏 printf("func DER_ItAsn1_WriteInteger() err:%d \n", ret); return ret; } pTmp = pTmp->next; //编码 p ret = EncodeChar(pTeacher->p, pTeacher->plen, &pTmp->next); if (ret != 0) { DER_ITCAST_FreeQueue(pHeadBuf); //释放链表 以免内存泄漏 printf("func EncodeChar() err:%d \n", ret); return ret; } pTmp = pTmp->next; //编码 plen ret = DER_ItAsn1_WriteInteger(pTeacher->plen, &(pTmp->next)); if (ret != 0) { DER_ITCAST_FreeQueue(pHeadBuf); //释放链表 以免内存泄漏 printf("func DER_ItAsn1_WriteInteger() err:%d \n", ret); return ret; } ret = DER_ItAsn1_WriteSequence(pHeadBuf, &pOutData); if (ret != 0) { DER_ITCAST_FreeQueue(pHeadBuf); //释放链表 以免内存泄漏 printf("func DER_ItAsn1_WriteSequence() err:%d \n", ret); return ret; } *outData = pOutData; //老师结构体 大节点 传出来 DER_ITCAST_FreeQueue(pHeadBuf); //释放链表 以免内存泄漏 /* *out = (unsigned char *)malloc(pOutData->dataLen); if (*out == NULL) { ret = 2; DER_ITCAST_FreeQueue(pOutData); printf("func TeacherEncode() malloc err:%d \n", ret); return ret; } memcpy(*out, pOutData->pData, pOutData->dataLen); //把运算结果copy到新分配的内存块中 *outlen = pOutData->dataLen; */ //DER_ITCAST_FreeQueue(pOutData); return ret; } int TeacherDecode_(unsigned char *indata, int inLen, Teacher **pStruct) { int ret = 0; ITCAST_ANYBUF *pTmp = NULL, *pHead = NULL; ITCAST_ANYBUF *pOutData = NULL; ITCAST_ANYBUF *inAnyBuf = NULL; Teacher *pTmpStru = NULL; /* //把c语言buf 转化成 ITCAST_ANYBUF inAnyBuf = (ITCAST_ANYBUF *)malloc(sizeof(ITCAST_ANYBUF)); if (inAnyBuf == NULL) { ret = 1; printf("func TeacherDecode malloc error: %d", ret); return ret; } memset(inAnyBuf, 0, sizeof(ITCAST_ANYBUF)); inAnyBuf->pData = (unsigned char *)malloc(inLen); if (inAnyBuf->pData == NULL) { DER_ITCAST_FreeQueue(inAnyBuf); ret = 2; printf("func TeacherDecode malloc error: %d", ret); return ret; } memcpy(inAnyBuf->pData, indata, inLen); inAnyBuf->dataLen = inLen; */ //将 ber 格式的字节流,转换成AnyBuf ret = DER_ITCAST_String_To_AnyBuf(&inAnyBuf, indata, inLen); if (ret != 0) { printf("func DER_ITCAST_String_To_AnyBuf error : %d", ret); return ret; } //解码 Teacher 结构体 得到4个节点 ret = DER_ItAsn1_ReadSequence(inAnyBuf, &pHead); if (ret != 0) { DER_ITCAST_FreeQueue(inAnyBuf); printf("func DER_ItAsn1_ReadSequence() err:%d \n", ret); return ret; } DER_ITCAST_FreeQueue(inAnyBuf); //把老师结构体给分配出来 pTmpStru = (Teacher *)malloc(sizeof(Teacher)); if (pTmpStru == NULL) { ret = 3; DER_ITCAST_FreeQueue(pHead); //释放链表 printf("func malloc() err:%d \n", ret); return ret; } memset(pTmpStru, 0, sizeof(Teacher)); pTmp = pHead; //解码 name ret = DER_ItAsn1_ReadPrintableString(pTmp, &pOutData); if (ret != 0) { DER_ITCAST_FreeQueue(pHead); //释放链表 printf("func DER_ItAsn1_ReadPrintableString() err:%d \n", ret); return ret; } //给name变量赋值 memcpy(pTmpStru->name, pOutData->pData, pOutData->dataLen); pTmp = pTmp->next; //跳到下一个节点 DER_ITCAST_FreeQueue(pOutData); //解码age ret = DER_ItAsn1_ReadInteger(pTmp, &(pTmpStru->age)); if (ret != 0) { DER_ITCAST_FreeQueue(pHead); //释放链表 printf("func DER_ItAsn1_ReadInteger() err:%d \n", ret); return ret; } pTmp = pTmp->next; //跳到下一个节点 //解码 p ret = DER_ItAsn1_ReadPrintableString(pTmp, &pOutData); if (ret != 0) { DER_ITCAST_FreeQueue(pHead); //释放链表 printf("func DER_ItAsn1_ReadPrintableString() err:%d \n", ret); return ret; } pTmpStru->p = (char *)malloc(pOutData->dataLen + 1); if (pTmpStru->p == NULL) { ret = 4; printf("func malloc err:%d \n", ret); return ret; } memcpy(pTmpStru->p, pOutData->pData, pOutData->dataLen); pTmpStru->p[pOutData->dataLen] = '\0'; pTmp = pTmp->next; //跳到下一个节点 DER_ITCAST_FreeQueue(pOutData); //解码 plen ret = DER_ItAsn1_ReadInteger(pTmp, &(pTmpStru->plen)); if (ret != 0) { DER_ITCAST_FreeQueue(pHead); //释放链表 printf("func DER_ItAsn1_ReadInteger() err:%d \n", ret); return ret; } *pStruct = pTmpStru; //间接赋值 return ret; } int TeacherDecode_Free(Teacher **pStruct) { Teacher *tmp = NULL; if (pStruct == NULL) { return 0; } tmp = *pStruct; if (tmp != NULL) { free(tmp->p); free(tmp); } *pStruct = NULL; // 1 不但把指针所指向的内存给释放掉 2 同时把实参赋值 NULL 避免野指针 return 0; } // 对 type 和 Teacher 结构体进行编码 封装。 int MsgEncode( void *pStruct, /*in*/ int type, unsigned char **outData, /*out*/ int *outLen) { ITCAST_ANYBUF *pHeadbuf = NULL, *pTemp = NULL; ITCAST_ANYBUF *pOutData = NULL; int ret = 0; if (pStruct == NULL && type < 0 || outData == NULL || outLen == NULL) { ret = KeyMng_ParamErr; printf("func MsgEncode() err:%d \n", ret); return ret; } ret = DER_ItAsn1_WriteInteger(type, &pHeadbuf); if (ret != 0) { printf("func DER_ItAsn1_WriteInteger() err:%d \n", ret); return ret; } switch (type) { case ID_MsgKey_Teacher: //编码老师 //Teacher_Encode(&t1, &myOut, &mOutlen); ret = TeacherEncode_((Teacher *)pStruct, &pTemp); break; case ID_MsgKey_Req: //密钥请求报文 编码 //ret = MsgKey_ReqEncode(密钥请求结构体, Anybuf); break; case ID_MsgKey_Res: //密钥应答报文 编码 //ret = MsgKey_ResEncode(密钥请求结构体, Anybuf); break; default: ret = KeyMng_TypeErr; printf("类型输入失败() itype:%d err:%d \n", type, ret); break; } if (ret != 0) { DER_ITCAST_FreeQueue(pHeadbuf); printf("编码失败err:%d \n", ret); return ret; } pHeadbuf->next = pTemp; //对type 和 结构体 再做一次 TLV ret = DER_ItAsn1_WriteSequence(pHeadbuf, &pOutData); if (ret != 0) { DER_ITCAST_FreeQueue(pHeadbuf); printf("func DER_ItAsn1_WriteSequence() err:%d \n", ret); return ret; } DER_ITCAST_FreeQueue(pHeadbuf); *outData = (unsigned char *)malloc(pOutData->dataLen); if (*outData == NULL) { DER_ITCAST_FreeQueue(pOutData); ret = KeyMng_MallocErr; printf("malloc() err:%d \n", ret); return ret; } memcpy(*outData, pOutData->pData, pOutData->dataLen); *outLen = pOutData->dataLen; DER_ITCAST_FreeQueue(pOutData); return ret; } int MsgDecode( unsigned char *inData,/*in*/ int inLen, void **pStruct /*out*/, int *type /*out*/) { ITCAST_ANYBUF *pHeadBuf = NULL, *inAnyBuf = NULL; int ret = 0; unsigned long itype = 0; //将 ber 格式的字节流,转换成 AnyBuf ret = DER_ITCAST_String_To_AnyBuf(&inAnyBuf, inData, inLen); if (ret != 0) { printf("func DER_ITCAST_String_To_AnyBuf error : %d", ret); return ret; } ret = DER_ItAsn1_ReadSequence(inAnyBuf, &pHeadBuf); if (ret != 0) { printf("func DER_ItAsn1_ReadSequence() err:%d \n", ret); return ret; } DER_ITCAST_FreeQueue(inAnyBuf); //解析 type ret = DER_ItAsn1_ReadInteger(pHeadBuf, &itype); if (ret != 0) { DER_ITCAST_FreeQueue(pHeadBuf); printf("func DER_ItAsn1_ReadInteger() err:%d \n", ret); return ret; } switch (itype) { case ID_MsgKey_Teacher: //解码 老师结构体 ret = TeacherDecode_(pHeadBuf->next->pData, pHeadBuf->next->dataLen, (Teacher **)pStruct); break; case ID_MsgKey_Req: //解码 请求报文 //ret = MsgKey_ReqDecode(pHeadBuf->next->pData, pHeadBuf->next->dataLen, (MsgKey_Req **)pStruct); break; case ID_MsgKey_Res: //解码 应答报文 break; default: ret = KeyMng_TypeErr; printf("itype:%d失败 :%d \n", itype, ret); break; } if (ret != 0) { DER_ITCAST_FreeQueue(pHeadBuf); return ret; } *type = itype; DER_ITCAST_FreeQueue(pHeadBuf); return ret; } int MsgMemFree(void **point, int type) { if (point == NULL) { return 0; } if (type == 0) //释放 Encode编码以后的内存块 { if (*point) free(*point); *point = NULL; return 0; } switch (type) { case ID_MsgKey_Teacher: TeacherDecode_Free((Teacher **)point); break; case ID_MsgKey_Req: //MsgKey_Req_Free((Teacher **)point); break; default: break; } return 0; }
2、wind下制作动态库原理
》动态库基本常识:
1)wind环境下,动态库—— xxx.dll xxx.lib xxx.h
Linux环境下,动态库—— xxx.so xxx.h -L -l 动态链接器 LD_LIBRARY_PATH ldd keymngserver ====> not found
链接器和动态链接器的区别和说明?
链接器: 工作于 可执行文件 编译期间。 -L -l 给链接器指定 路径和文件名。
动态链接器: 工作与 可以行文件 运行期间。
2)动态库不是一个exe。是系列函数的集合(二进制)。 keyMsg_Encode.c
按某种规范制作,被操作系统加载。 VS -- ldd
如: 111.exe --- aaa.dll
系统在加载111.exe时,检查111.exe运行是否需要额外的动态库。
若需要,则按一定规则加载。找到成功加载,找不到。报错。----指定动态库路径
3、wind动态库制作
》wind动态库的制作:
动态库的命名,不要使用中文。
创建时选择.dll生成项目。 但,不能运行。
动态库API函数的 导出。
只将用户使用的指定某几个函数从动态库中导出。 --- xxx.lib
使用 __declspec(dllexport) 宏,提示编译器。在编译程序的时候,需特殊处理的函数。
注意:制作库时 .c 文件 和 .h 文件 均需要添加该宏!!!
默认生成的动态库位于上层目录下的 Debug 目录内。动态库生成后不能改名字!!!
》动态库制作结果:
xxxxx.dll 二进制目标函数集,可被操作系统加载
xxxxx.lib 是对dll的描述文件 (包含多少函数,哪些被导出。。。。)
xxx.h 以及以上2个文件 即可为exe执行提供支持。 但提供给用户时, .h中的 __declspec(dllexport) 宏应删除。
》动态库调用和调试:
通知VS2013编译器,使用提供的动态库。
将.dll .lib .h三个文件放置在 .c同级目录下。
右键项目“属性”---“配置属性”---“链接器”---“输入”--- “附加依赖项”---下拉菜单“编辑”
----输入【描述dll信息lib文件 xxxxx.lib】
》常见错误:
1)若xxx.lib配置错误,则VS编译器无法找到所用函数。
2)如若在运行111.exe时,说明操作系统找不到动态库加载路径。
1. 配置PATH 2. 将exe和动态库dll置于同一目录下即可。
》VS2015(lib和dll正确放置位置):
xxxx.lib ---> .c 源码 .h 头文件所在目录位置
xxxx.dll ---> .exe 可执行文件所在目录位置。
练习:
》制作DLL:
(0)新建一个文件夹testDll,把(itcast_asn1_der.c、itcast_asn1_der.h、itcastderlog.c、itcastderlog.h、keymng_msg.c、keymng_msg.h、keymng_msg_test.c)7个文件拷贝至testDll下,以待使用。
(1)打开“VS2015”,“新建项目”,左侧选择“Visual C++”,右侧选择“Win32 项目”,起名称,不含中文(如:testMakeDll),设置项目位置(如:D:\WorkSpace\VS2015\Projects\SafeTrassion\),点击“确定”,然后在“应用程序设置”中:“应用程序类型”选择“DLL”,“附加选项”去掉勾选除“空项目”外的其他选项,最后点击“完成”。
(2)在左侧“解决方案资源管理器”中“testMakeDll”下的“源文件”右键“添加新建项”,如:test.c,然后输入:
int main() { return 0; }
点击“运行”,会报错如下:(验证了dll不能运行)
(3)在左侧“解决方案资源管理器”中“testMakeDll””右键“在文件资源管理器中打开文件夹”,然后将testDll(itcast_asn1_der.c、itcast_asn1_der.h、itcastderlog.c、itcastderlog.h、keymng_msg.c、keymng_msg.h)6个文件拷贝至此文件夹(注意:拷贝之前一定要先测试写的代码是否有错误,如果有错误,编译的DLL就是错误的,后期使用就会出问题。)“testMakeDll””右键“添加现有项”然后添加这6个文件。然后把之前测试的test.c删除掉。
(4)在“keymng_msg.c”和“keymng_msg.h”中对需要导出使用的函数前添加宏__declspec(dllexport);
注意: 制作动态库时.c 文件 和 .h 文件 均需要添加__declspec(dllexport)宏!!!
(5)点击“运行”,生成动态库(注意:虽然不能运行,但成功制作了动态库)。
动态库在(在左侧“解决方案资源管理器”中“testMakeDll””右键“在文件资源管理器中打开文件夹”)的上一级的“Debug”目录下会有“testMakeDll.dll”和“testMakeDll.lib”
注意:如果缺少第(4)步,将会无法产生“testMakeDll.lib”文件!
(6)把(“testMakeDll.dll”和“testMakeDll.lib”)2个文件拷贝至testDll下,以待使用,加上之前的7个文件,testDll下共9个文件。
注意:testDll中的头文件keymng_msg.h中不能添加宏__declspec(dllexport);
》测试:
(7)重新打开“VS2015”,“新建项目”,左侧选择“Visual C++”,右侧选择“空项目”,起名称,不含中文(如:testDll),设置项目位置(如:D:\WorkSpace\VS2015\Projects\SafeTrassion\),最后点击“确定”。
(8)将testDll下的keymng_msg_test.c拷贝至(在左侧“解决方案资源管理器”中“testMakeDll””右键“在文件资源管理器中打开文件夹”);然后把keymng_msg.h拷贝至此文件夹,再把(“testMakeDll.dll”和“testMakeDll.lib”)拷贝至此文件夹。在项目“testDll”右键“添加现有项”,然后添加keymng_msg.h和keymng_msg_test.c;
(9)在项目“testDll”右键“属性”,然后左侧选择“链接器”下的“输入”,右侧选择“附加依赖项”后边的下拉条,然后点击“编辑”,在第1个框输入:testMakeDll.lib,点击“确定”,然后点击“应用”;
(10)打开keymng_msg_test.c,然后点击“运行”。
注意:最好testMakeDll.lib ---> .c 源码 .h 头文件所在目录位置(在左侧“解决方案资源管理器”中“testMakeDll””右键“在文件资源管理器中打开文件夹”),testMakeDll.dll ---> .exe 可执行文件所在目录位置(上一级的“Debug”目录下),而上一级的Debug目录只有在运行代码后才会产生。
》动态库内存释放:
遵循一个原则:谁分配,谁释放。
调用动态库函数分配的内存,也一定要调用动态库函数来释放内存。
所以,在keymng_msg.c和keymng_msg.h不能只给用户提供MsgEncode和MsgDecode,而不提供MsgMemFree!
4、库使用问题总结
》__declspec(dllexport) 宏的使用总结:
(1)制作 dll库的时候:.c 和 .h 都添加。
(2)使用 dll 库的是:.h 中 不添加。
》注意两个小问题:
(1)在左侧选择“链接器”下的“输入”,右侧选择“附加依赖项”后边的下拉条,然后点击“编辑”,在第1个框输入:testMakeDll.dll而不是testMakeDll.lib;运行程序keymng_msg_test.c时会报错“LINK 1104 无法打开文件“testMakeDll.dll””
(2)可以将testMakeDll.lib放至资源管理器“testDll”下的“资源文件”中,不过不推荐。
5、Linux动态库制作理论
》Linux 动态库制作:
lib库名.so xxx.h
(1)制作 Linux 动态库
1)gcc -c itcast_asn1_der.c -o itcast_asn1_der.o -fPIC (生成与位置无关的代码) ./a.out 运行之后 加载。 延迟载入。
-c itcastderlog.c
-c keymng_msg.c
链接: 数据段合并 和 地址回填(数据地址重定位) main + xxxxx
ar 制作静态库。
2)gcc -shared -o lib库名.so itcast_asn1_der.o itcastderlog.o keymng_msg.o ----> lib库名.so
(2)动态库使用:
lib库名.so 文件 本身不能执行。 和wind下的xxxx.dll 一样,都不能执行
项目目录/ src/ -- xxx.c
obj/ -- xxx.o 用于调试
inc/ -- xxx.h 头文件
lib/ -- xxx.so/xxx.a 库
media/ -- 媒体文件
makefile
gcc test.c -o a.out -L 指定库所在路径 -l (小写L)指定库名(去掉lib, 去掉.so) -I (大写i)指定库所对应的头文件所在目录 ---> a.out --> ./a.out
链接器:工作于连接生成可执行文件阶段。 gcc 第 4 步。 任务:数据段合并 和 地址回填(数据地址重定位)
用 -L -l -I 参数。
动态链接器:工作于 加载器加载可执行文件到内存后,程序运行时查找库函数期间。 加载库函数到内存,重定位函数地址。(绝对地址)
用 LD_LIBRARY_PATH 。 ldd ./a.out ===> not found
运行 : ./a.out ----》 报错! --》 原因: 动态连接器,找不到加载动态库的目录位置。 原因就是ldd a.out 找不到: ===> not found
错误提示:./a.out: error while loading shared libraries: libmymessagereal.so: cannot open shared object file: No such file or directory
-l、-L 给 “连接器” 指定动态库所在位置。
》解决:
1)将自定义的动态库 cp 放到 标准C库 ( libc ) 所在目录位置;(不推荐)
2)在可执行文件所在目录位置 执行命令: export LD_LIBRARY_PATH = 库路径; (临时生效,环境变量跟进程走,每个进程有属于该进程自己唯一的环境变量,当终端结束了,进程的地址空间消失了,环境变量就没有了。)
3)帮助动态连接器 指定加载 动态库目录位置。 export LD_LIBRARY_PATH = 库路径; 写入 ~/.bash_profile (配置文件)中。(用户启动时,加载配置文件)
4) ... 修改系统级下的系统环境变量(不推荐)
5) ... 把动态库的位置编译时编写固定死到可执行程序当中。(不推荐)
可具体参考APUE(Advanced Programming in the UNIX Environment)书
ldd a.out 成功显示动态库位置。 ./a.out 运行。
6、Linux下动态库制作
》练习:
>mkdir createLib
>cd createLib
(itcast_asn1_der.c、itcast_asn1_der.h、itcastderlog.c、itcastderlog.h、keymng_msg.c、keymng_msg.h)6个文件在wind通过远程工具拷贝至createLib文件夹
>gcc -c *.c -fPIC
两个警告不用管!
>gcc -shared -o libmymessage.so *.o
>ls
>mkdir test
(keymng_msg_test.c)1个文件在wind通过远程工具拷贝至test文件夹
a.out存在了,但是无法直接执行
>ldd a.out(可知,自己制作的动态库找不到)
法一:(不推荐)
缺点:断开连接后重连仍然无法执行。
法二:修改~/.bash_profile(加入:../)
但是这种不规范,所以应该:
将test目录下的*.c移到上一级目录,然后删除test(rm test)
>gcc keymng_msg_test.c -o a.out -L ./lib -lmymessage -I./inc
~/.bash_profile(加入/lib:,第一个前不用加:,加入其后写:./lib)
注意:这样写相对路径,以后只要按照标准规范(按相应的C项目组织库—把库放入lib文件夹下),~/.bash_profile就不用修改了!
>ldd a.out
7、Makefile复习
>mkdir testMake
将*.c拷贝至testMake目录下
>mkdir test
>cd test
>vi test.c
int main(void) { return 0; }
>vi makefile
a.out:test.c gcc test.c -o a.out
>make
将*.h拷贝至testMake目录下
查看testMake下(itcast_asn1_der.c、itcast_asn1_der.h、itcastderlog.c、itcastderlog.h、keymng_msg.c、keymng_msg.h)6个文件
>gcc itcast_asn1_der.c itcastderlog.c keymng_msg.c keymng_msg_test.c -o a.out
>./a.out
>vi makefile
a.out:itcast_asn1_der.c itcastderlog.c keymng_msg.c keymng_msg_test.c gcc itcast_asn1_der.c itcastderlog.c keymng_msg.c keymng_msg_test.c -o a.out
>rm a.out
>make
缺点:这种makefile,改动任何一个源程序,都需要重新编译,而且增加或删除代码文件,makefile需要重新制作。
第一次优化:
makefile :
gcc -c itcast_asn1_der.c -o itcast_asn1_der.o 预处理、编译、汇编
gcc -c itcastderlog.c -o itcastderlog.o 预处理、编译、汇编
gcc -c keymng_msg.c -o keymng_msg.o 预处理、编译、汇编
gcc -c keymng_msg_test.c -o keymng_msg_test.o 预处理、编译、汇编
gcc itcast_asn1_der.o itcastderlog.o keymng_msg.o keymng_msg_test.o -o a.out
>vi makefile
a.out:itcast_asn1_der.o itcastderlog.o keymng_msg.o keymng_msg_test.o gcc itcast_asn1_der.o itcastderlog.o keymng_msg.o keymng_msg_test.o -o a.out itcast_asn1_der.o:itcast_asn1_der.c gcc -c itcast_asn1_der.c -o itcast_asn1_der.o itcastderlog.o:itcastderlog.c gcc -c itcastderlog.c -o itcastderlog.o keymng_msg.o:keymng_msg.c gcc -c keymng_msg.c -o keymng_msg.o keymng_msg_test.o:keymng_msg_test.c gcc -c keymng_msg_test.c -o keymng_msg_test.o clean: -rm -rf *.o a.out
>make clean -n(注意第一次执行用make clean -n,它可模拟执行,防止出错。)
>make clean(真正执行)
>make
如果某个.c或者.h更改了,需要全部重新编译.o;新增或删除文件,make指令失效
第二次优化:(1)解决只更改某个.c或者.h更改了,需要全部重新编译.o的问题(2)解决新增或删除文件,makefie失效的问题
》makefile语法:
1 条规则:
目标:依赖
命令
要求,目标必须晚于依赖条件生成时间。如果不满足,则更新目标。
如果依赖不存在,寻找新的规则生成依赖。
2 个函数:
$(wildcard 参): 获取指定类型特征的文件
src = $(wildcard *.c)
$(patsubst 参1, 参2, 参3):根据指定类型变量,获取新变量。
$(patsubst %.c, %.o, $(src)): 将参数3 中,包含参数1的部分,替换成参数2。
3 个自动变量:
$@: 在规则的 命令中, 表示目标。
$^: 在规则的 命令中, 表示 所有依赖条件
$<: 在规则的 命令中, 表示 第一个依赖条件。 如果是模式规则,会将依赖条件依次取出。 执行命令。
%.o: %.c
gcc -c $< -o $@
#src = itcast_asn1_der.c itcastderlog.c keymng_msg.c keymng_msg_test.c,src会成这样的字符串 src = $(wildcard *.c) #obj = itcast_asn1_der.o itcastderlog.o keymng_msg.o keymng_msg_test.o,obj会成这样的字符串 obj = $(patsubst %.c, %.o, $(src)) #ALL在于告诉makefile最后生成的目标是a.out ALL:a.out #a.out:itcast_asn1_der.o itcastderlog.o keymng_msg.o keymng_msg_test.o # gcc itcast_asn1_der.o itcastderlog.o keymng_msg.o keymng_msg_test.o -o a.out a.out:$(obj) gcc $^ -o $@ #itcast_asn1_der.o:itcast_asn1_der.c # gcc -c itcast_asn1_der.c -o itcast_asn1_der.o #itcastderlog.o:itcastderlog.c # gcc -c itcastderlog.c -o itcastderlog.o #keymng_msg.o:keymng_msg.c # gcc -c keymng_msg.c -o keymng_msg.o #keymng_msg_test.o:keymng_msg_test.c # gcc -c keymng_msg_test.c -o keymng_msg_test.o #模式规则,会将依赖条件依次取出。 依次执行gcc -c xx -o xxx这样的命令 %.o:%.c gcc -c $< -o $@ clean: -rm -rf $(obj) a.out
把注释去掉:然后把a.out用变量target代替(这样更改的好处是target后可以指定任意变量,如:app)
src = $(wildcard *.c) obj = $(patsubst %.c, %.o, $(src)) target = app ALL:a.out $(target):$(obj) gcc $^ -o $@ %.o:%.c gcc -c $< -o $@ clean: -rm -rf $(obj) $(target)
>make clean -n
>make clean
>rm a.out
>make(app生成了)
第三次优化:(解决touch clean后make clean失效)
添加.PHONY:clean ALL
src = $(wildcard *.c) obj = $(patsubst %.c, %.o, $(src)) target = app ALL:$(target) $(target):$(obj) gcc $^ -o $@ %.o:%.c gcc -c $< -o $@ clean: -rm -rf $(obj) $(target) .PHONY:clean ALL
第四次优化:静态模式规则
静态模式规则:
指定,针对某一个变量,使用特定的模式规则。
src = $(wildcard *.c) obj = $(patsubst %.c, %.o, $(src)) target = app ALL:$(target) $(target):$(obj) gcc $^ -o $@ $(obj)%.o:%.c gcc -c $< -o $@ clean: -rm -rf $(obj) $(target) .PHONY:clean ALL
8、Makefile项目目录管理
第五次优化:按照项目目录,把相应的文件放入相应的目录下;然后编译make失效
创建src、obj、inc目录,然后把*.c放入到src中(mv ./*.c src/),*.h放入到inc中(mv ./*.h inc/)
src = $(wildcard ./src/*.c) obj = $(patsubst ./src/%.c, ./obj/%.o, $(src)) target = app ALL:$(target) $(target):$(obj) gcc $^ -o $@ $(obj):./obj/%.o:./src/%.c gcc -c $< -o $@ clean: -rm -rf $(obj) $(target) .PHONY:clean ALL
>make(报错,原因头文件未指定!)
问题:头文件应该放在哪(-I头文件)?还是放到gcc obj/*.o -o app后边吗?
头文件.h的链接属于预处理步骤,所以应该放到gcc -c src/*.c -o obj/*.o处,而不应该放到gcc obj/*.o -o app后边。
src = $(wildcard ./src/*.c) obj = $(patsubst ./src/%.c, ./obj/%.o, $(src)) target = app inc_path = ./inc ALL:$(target) $(target):$(obj) gcc $^ -o $@ $(obj):./obj/%.o:./src/%.c gcc -c $< -o $@ -I $(inc_path) clean: -rm -rf $(obj) $(target) .PHONY:clean ALL
注意:inc_path不能写成PATH,会和环境变量重名而出错!
四、安全传输平台项目—统一通信组件
1、统一通信组件-客户端服务器API
统一通讯组件接口设计
》项目开发对通讯组件的要求:
>上层业务流和基础组件的合理分层
>Win和linux异构、跨平台
>稳定性
>连接的处理(短链接、长连接)
公网:断链修复
>公网:粘包处理
>入门的关键:连接>
》解决的问题:稳定、易用;长连接短链接,socket连接池;断链修复;跨平台;粘包;
》技术基础:
1)从linux内核的角度,理解三次握手和四次断开(全双工)
2)主动套接字和被动套接字,accept的函数
3)连接的概念
4)长连接和短链接实现的条件
a)客户端主动
b) 服务器端配合
c)问题:服务器端是如何判断对方已经关闭了那?
5)socket连接池的设计理念
》socket通信:
如何在服务器和客户端提高通信效率?
单纯提高服务器效率的方法有:多路I/O转接;线程池;事件if回调
如何提高客户端和服务器整体呢?提高客户端与服务器单位时间内的连接数量,引入连接池(可以节省建立连接和销毁连接的时间开销。每次从连接池取出一条连接(实际是文件描述符),用于与服务器通信,通信结束后,把连接再放回连接池。)
》统一通信函数设计:
连接池 初始化
int sockPool_init(int num, int ip, int port, void **handler);
获取一条连接:
int sockPool_get(void *handler, int *fd);
读取数据
int sockPool_recv(int fd, unsigned char **out, int *outLen);
发送数据
int sockPool_send(int fd, unsigned char *in, int inLen);
放回一条连接:
int sockPool_put(void *handler, int fd);
连接池 销毁
int sockPool_destroy(void *handler);
》socket连接池—poolsocket.h
#ifndef _poolsocket_H_ #define _poolsocket_H_ #ifdef __cplusplus extern 'C' { #endif //错误码定义 #define Sck_Ok 0 #define Sck_BaseErr 3000 #define Sck_ErrParam (Sck_BaseErr+1) #define Sck_ErrTimeOut (Sck_BaseErr+2) #define Sck_ErrPeerClosed (Sck_BaseErr+3) #define Sck_ErrMalloc (Sck_BaseErr+4) #define Sck_Err_Pool_CreateConn (Sck_BaseErr+20) //创建连接池 (没有达到最大连接数) #define Sck_Err_Pool_terminated (Sck_BaseErr+21) //已终止 #define Sck_Err_Pool_GetConn_ValidIsZero (Sck_BaseErr+22) //有效连接数是零 #define Sck_Err_Pool_HaveExist (Sck_BaseErr+22) //连接已经在池中 #define Sck_Err_Pool_ValidBounds (Sck_BaseErr+22) //有效连接数目超过了最大连接数 //客户端 初始化 int sckClient_init(); //客户端 连接服务器 int sckClient_connect(char *ip, int port, int connecttime, int *connfd); //客户端 关闭和服务端的连接 int sckClient_closeconn(int connfd); //客户端 发送报文 int sckClient_send(int connfd, int sendtime, unsigned char *data, int datalen); //客户端 接受报文 int sckClient_rev(int connfd, int revtime, unsigned char **out, int *outlen); //1 //客户端 释放 int sckClient_destroy(); //释放内存 int sck_FreeMem(void **buf); typedef struct _SCKClitPoolParam { char serverip[64]; int serverport; int bounds; //池容量 int connecttime; int sendtime; int revtime; }SCKClitPoolParam; //客户端 socket池初始化 int sckCltPool_init(void **handle, SCKClitPoolParam *param); //客户端 socket池 获取一条连接 int sckCltPool_getConnet(void *handle, int *connfd); //客户端 socket池 发送数据 int sckCltPool_send(void *handle, int connfd, unsigned char *data, int datalen); //客户端 socket池 接受数据 int sckCltPool_rev(void *handle, int connfd, unsigned char **out, int *outlen); //1 //客户端 socket池 把连接放回 socket池中 int sckCltPool_putConnet(void *handle, int connfd, int validFlag); //0正常 1 //客户端 socket池 销毁连接 int sckCltPool_destroy(void *handle); //服务器端初始化 int sckServer_init(int port, int *listenfd); int sckServer_accept(int listenfd, int timeout, int *connfd); //服务器端发送报文 int sckServer_send(int connfd, int timeout, unsigned char *data, int datalen); //服务器端端接受报文 int sckServer_rev(int connfd, int timeout, unsigned char **out, int *outlen); //1 int sckServer_close(int connfd); //服务器端环境释放 int sckServer_destroy(); #ifdef __cpluspluse } #endif #endif
2、统一通信组件-服务器实现
编写server.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include "poolsocket.h" void *start_routine(void * arg) { int ret; int timeout = 3; int connfd = (int)arg; unsigned char *out = NULL; int outlen = 0; LABLE: //服务器端端接受报文 ret = sckServer_rev(connfd, timeout, &out, &outlen); if (ret == Sck_ErrPeerClosed) { // 检测到 对端关闭,关闭本端。 sckServer_close(connfd); printf("---ErrPeerClosed \n"); return NULL; } else if (ret == Sck_ErrTimeOut) { printf("---服务器检测到客户端发送数据 超时 \n"); goto LABLE; } else if (ret != 0) { printf("未知错误\n"); return NULL; } // 处理数据。 ----- 回射 printf("客户端发送了:%s\n", out); //服务器端发送报文 ret = sckServer_send(connfd, timeout, out, outlen); if (ret == Sck_ErrPeerClosed) { // 检测到 对端关闭,关闭本端。 sckServer_close(connfd); printf("---ErrPeerClosed \n"); return NULL; } else if (ret == Sck_ErrTimeOut) { printf("---服务器检测到本端发送数据 超时 \n"); return NULL; } else if (ret != 0) { printf("未知错误\n"); return NULL; } return NULL; } int main(void) { int listenfd; int port = 8080; int ret = 0; int timeout = 3; int connfd = -1; pthread_t pid; //服务器端初始化 ret = sckServer_init(port, &listenfd); if (ret != 0) { printf("sckServer_init error %d\n", ret); return ret; } while (1) { ret = sckServer_accept(listenfd, timeout, &connfd); if (ret == Sck_ErrTimeOut){ continue; } else if(ret != 0) { printf("sckServer_accept error %d\n", ret); return ret; } ret = pthread_create(&pid, NULL, start_routine, (void *)connfd); } //服务器端环境释放 sckServer_destroy(); return 0; }
1)需要连接库-libitcastsocket.so
2)>gcc server.c -o server -L ../ -litcastsocket -I../ -pthread
3)客户端需要建立连接,打开另一个终端,输入>./client 127.0.0.1 8080
4)服务器端打印出来的代码乱码问题
原因分析:wind使用的UltraEdit,中文的字符编码,到Linux编解码对应不上,所以乱码。
在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
posted on 2020-07-25 18:36 Alliswell_WP 阅读(618) 评论(0) 编辑 收藏 举报