• 安全传输平台项目——统一报文-动态库制作-统一通信组件-函数接口


    在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主: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.h

    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 
    ", 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 
    ", 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 
    ", 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 
    ", 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 
    ", ret);
            return ret;
        }
    
        ret = DER_ItAsn1_WriteSequence(pHeadBuf, &pOutData);
        if (ret != 0)
        {
            DER_ITCAST_FreeQueue(pHeadBuf); //释放链表 以免内存泄漏
            printf("func DER_ItAsn1_WriteSequence() err:%d 
    ", 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 
    ", 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 
    ", 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 
    ", 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 
    ", 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 
    ", 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 
    ", ret);
            return ret;
        }
        pTmpStru->p = (char *)malloc(pOutData->dataLen + 1);
        if (pTmpStru->p == NULL)
        {
            ret = 4;
            printf("func malloc err:%d 
    ", ret);
            return ret;
        }
        memcpy(pTmpStru->p, pOutData->pData, pOutData->dataLen);
        pTmpStru->p[pOutData->dataLen] = '';
    
    
        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 
    ", 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 
    ", ret);
            return ret;
        }
    
        ret = DER_ItAsn1_WriteInteger(type, &pHeadbuf);
        if (ret != 0)
        {
            printf("func DER_ItAsn1_WriteInteger() err:%d 
    ", 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 
    ", type, ret);
            break;
        }
    
        if (ret != 0)
        {
            DER_ITCAST_FreeQueue(pHeadbuf);
            printf("编码失败err:%d 
    ", 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 
    ", 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 
    ", 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 
    ", 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 
    ", 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 
    ", 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;
    }
    keymng_msg.c

    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:WorkSpaceVS2015ProjectsSafeTrassion),点击“确定”,然后在“应用程序设置”中:“应用程序类型”选择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:WorkSpaceVS2015ProjectsSafeTrassion),最后点击“确定”。

    (8)将testDll下的keymng_msg_test.c拷贝至在左侧“解决方案资源管理器”中“testMakeDll””右键“在文件资源管理器中打开文件夹”);然后把keymng_msg.h拷贝至此文件夹,再把(“testMakeDll.dll”和“testMakeDll.lib”)拷贝至此文件夹。在项目“testDll”右键“添加现有项”,然后添加keymng_msg.hkeymng_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
    poolsocket.h

    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 
    ");
            return NULL;
        } else if (ret == Sck_ErrTimeOut) {
            printf("---服务器检测到客户端发送数据 超时 
    ");
            goto LABLE;
        } else if (ret != 0) {
            printf("未知错误
    ");
            return NULL;
        }
    
        // 处理数据。 ----- 回射
        printf("客户端发送了:%s
    ", out);
    
         //服务器端发送报文
        ret = sckServer_send(connfd, timeout, out, outlen);
         if (ret == Sck_ErrPeerClosed) {
            // 检测到 对端关闭,关闭本端。
            sckServer_close(connfd);
            printf("---ErrPeerClosed 
    ");
            return NULL;
        } else if (ret == Sck_ErrTimeOut) {
            printf("---服务器检测到本端发送数据 超时 
    ");
            return NULL;
        } else if (ret != 0) {
            printf("未知错误
    ");
            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
    ", 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
    ", ret);    
                return ret;
            }
            ret = pthread_create(&pid, NULL, start_routine, (void *)connfd);                    
        }
         
         //服务器端环境释放 
        sckServer_destroy();
    
        return 0;    
    }
    server.c

    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,转载请注明出处。

  • 相关阅读:
    svn 如果遇到an unversioned directory of the same name already exists的解决办法
    记一次keepalived脑裂问题查找
    zabbix3.2部署
    mysql配置文件
    CentOS 6.6 搭建Zabbix 3.0.3 过程
    CDN网络原理
    Vmware ESXi 6.5 安装手册
    Out of resources when opening file ‘./xxx.MYD’ (Errcode: 24)解决方法
    MongoDB主从复制,主主复制
    mysql主从复制跳过错误
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_SecureTransmissionPlatform_Project03.html
Copyright © 2020-2023  润新知