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

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

posted on 2020-07-25 18:36  Alliswell_WP  阅读(618)  评论(0编辑  收藏  举报

导航