安全传输平台项目扩展——C复习-C++复习-keymngclient重构

在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

11-安全传输平台项目扩展-第01天(C复习-C++复习-keymngclient重构)

目录:
一、课程目标
二、安全传输平台项目扩展——C复习-C++复习
1、C语言知识体系复习-两个模型
2、C语言知识体系复习-指针做函数api实现
3、C语言知识体系复习-模拟函数调用入栈出栈内存模型
4、C语言知识体系复习-间接赋值的重要性和成立三个条件
5、C++语言知识体系复习-多态理解1
6、C++语言知识体系复习-多态理解2
7、C++语言知识体系复习-C和C++横向比较
8、C语言知识体系复习-回调函数
9、C++面向抽象类编程思想回顾
三、安全传输平台项目扩展——keymngclient重构
1、项目需求和方案
2、C++类对象之间的关系-依赖和关联
3、密钥协商客户端业务流复习
4、keymngclient设计与实现-思路
5、keymngclient设计与实现-keymngclient的hello
6、keymngclient设计与实现-LogHelper类
7、keymngclient设计与实现-myipc类
8、keymngclient设计与实现-KeyMng_ShmOp类
9、keymngclient设计与实现-应用程序框架类和业务流类设计思想
10、keymngclient设计与实现-keymngclientapp
11、keymngclient设计与实现-keymngclientop
12、keymngclient设计与实现-初始化流程编写
13、keymngclient设计与实现-初始化流程调试
14、keymngclient设计与实现-密钥协商编写和调试

 

一、课程目标

用C++重构安全传输平台

深入理解C语言知识体系

两个模型(函数调用、内存四区模型)
    指针做函数参数(一级指针、二级指针、三级指针;指针的输入和输出)
    函数指针做函数参数
    C语言项目开发理念:接口的封装和设计、模块之间解耦合

深入理解C++语言知识体系
    封装、继承、多态
项目开发中C++工具的应用    
    C++项目开发理念(面向抽象类编程)
进一步理解安全传输平台secmngclient和secmngserver的业务模型
    用C++做开发常见套路

 

二、安全传输平台项目扩展——C复习-C++复习

1、C语言知识体系复习-两个模型

》两个模型(函数调用、内存四区模型)

示例:main函数调用fun1()函数,fun1()函数调用fun2()函数,fun2()函数调用fun3()函数:

问题1:在函数main中分配的内存,在fun3()中能用吗?

都能用

问题2:在函数fun3()中分配的内存 在main函数中能用吗?

内存  堆  栈  全局区(静态变量) 代码区——要看分配的内存在哪,全局和malloc(栈)得到的可以用

》理念:输入和输出

角度:站在被调用函数的调度去思考  函数1调用函数2

  输入:函数1调用函数2,在函数1中分配内存,供函数2使用

  输出:函数1调用函数2,在函数2中分配内存,供函数1使用

 

2、C语言知识体系复习-指针做函数api实现

》指针做函数参数(一级指针、二级指针、三级指针;指针的输入和输出)

案例1:指针做函数参数

需求:写一个接口 完成配置文件的读 要求一次性把N行数据 返给调用者

>dm01_指针做函数参数.c

#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "string.h"


//间接赋值成立的三个条件
//条件1: 定义两个变量 (形参 实参)
//条件2: 建立关联 若为函数调用 实参取地址 传给  形参 
//条件3: 在被调用函数中  *p 实参的地址 去 间接的修改实参的值 
//  用N级形参 去修改 N-1级实参的值

//写一个接口 完成配置文件的读 要求一次性把N行数据 返给调用者
int  getFileContent(char *pFileName/*in*/, char ***p, int *nLine)
{
    char **tmpP = NULL;
    int        i = 0;
    tmpP = (char **)malloc(sizeof(char *)* 10);
    if (tmpP == NULL)
    {
        printf("func getFileContent() err: ");
        return -1;
    }


    for (i = 0; i < 10; i++)
    {
        tmpP[i] = (char *)malloc(20);
        sprintf(tmpP[i], "%d%d%d", i, i, i);
    }

    //间接赋值
    *p = tmpP;  //*p实参的地址 放在=的左边 去间接的修改 实参的值
    //*p1 = *p2;
    *nLine = 10;
    return 0;
}

int  getFileContent_Free(char **p, int iLine)
{
    int i = 0;
    if (p == NULL)
    {
        return 0;
    }

    for ( i = 0; i < iLine; i++)
    {
        free(p[i]);
    }

    free(p);
    //p = NULL;

    return 0;
}


// 既可以把指针所指向的内存空间给释放掉 并且把实参重新赋值成NULLL
int  getFileContent_Free2(char ***p, int iLine)
{
    getFileContent_Free(*p, iLine);
    *p = NULL;
    return 0;
}


int main()
{
    int        ret = 0, i = 0;
    char    *myFileName = "c:\1.txt";
    char    **myP = NULL;
    int        myLine = 0;

    printf("hello...\n");

    //获取文件内容
    ret = getFileContent(myFileName, &myP, &myLine);
    if (ret != 0)
    {
        printf("func getFileContent() err:%d \n", ret);
        return ret;
    }

    //打印文件内容 按照行
    for (i = 0; i < myLine; i++)
    {
        printf("%s\n", myP[i]);
    }


    //getFileContent_Free(myP, myLine);
    getFileContent_Free2(&myP, myLine);
    system("pause");
    return 0;
}
dm01_指针做函数参数.c

问题:为什么getFileContent_Free中释放完内存后不能p = NULL?

直接修改p的值,时无法传给(main函数)实参的,所以getFileContent_Free2中*p实参的地址 放在=的左边可以去间接的修改 实参的值。为加深理解:譬如可以在main函数中通过myP=0x11直接修改实参的值,也可以通过*(&myP)=0x11间接修改实参的值,只不过是把*(&myP)=0x11放到被调用函数getFileContent_Free2中,而在被调用函数中getFileContent_Free使用(&myP)=0x11是无法修改实参的值的。所以在getFileContent_Free中修改p的值(p=NULL),与实参没有任何关系(更改不掉的),反而实参指向的空间因为没有了,会出现野指针。

 

3、C语言知识体系复习-模拟函数调用入栈出栈内存模型

》dm01_指针做函数参数.c 函数调用模型

注意:变量的本质:门牌号(内存的标号)

给变量赋值:给变量所代表的内存空间赋值。

 

4、C语言知识体系复习-间接赋值的重要性和成立三个条件

》间接赋值成立的三个条件

条件1:定义两个变量 (形参 实参)
条件2:建立关联 若为函数调用 实参取地址 传给  形参
条件3:在被调用函数中  *p 实参的地址 去 间接的修改实参的值
用N级形参 去修改 N-1级实参的值

 

5、C++语言知识体系复习-多态理解1

》多态成立的三个条件
要继承、虚函数重写、父类指针(引用)指向子类对象
效果:同一个调用语句 可以有多种形态(多种调用方法)

多态的意义?要从3大理念看。
》面向对象的三大理念  
封装:类的对象做函数参数的角度1 突破了C语言函数的概念
继承:可以使用老爹的东西
多态:老爹可以使用后来人写的代码  可扩展 模块的解耦合

》多态的现象: 同一个调用语句 可以有多种形态

扔过来一个子类对象 执行子类API函数
扔过来一个父类对象 执行父类API函数

》C++编译器如何实现多态?

1)提前布局 2)迟绑定(动态联编)

说明:C++编译器为含有虚函数的类的对象提前布局vptr指针和虚函数表 ;在发生多态的时候 (虚函数调用的时候), 去虚函数表中查找调用地址(函数的入口地址);执行后来人写的代码。

 

6、C++语言知识体系复习-多态理解2

练习:dm02_多态理解.cpp

#include <iostream>
using namespace std;


//多态成立的三个条件
// 要继承  虚函数重写  父类指针(引用)指向子类对象 
//效果: 同一个调用语句 可以有多种形态(多种调用方法)

//多态的意义 
//面向对象的三大理念  
//封装    类的对象做函数参数的角度1 突破了C语言函数的概念
//继承     可以使用老爹的东西 
//多态    老爹可以使用后来人写的代码  可扩展 模块的解耦合

//1 提前布局 2 迟绑定(动态联编)


class MyParent
{
public:
    MyParent()
    {
        cout << "我是爹 构造" << endl;
    }
public:
    virtual void print()
    {
        cout << "我是爹 working" << endl;
    }
protected:
private:
    int  a;
};


class MyChhild : public MyParent
{
public:
    MyChhild()
    {
        cout << "我是儿子 构造" << endl;
    }
public:
    virtual void print()
    {
        cout << "我是儿子 working" << endl;
    }
protected:
private:
};


//10:35写
class MyChhildChild : public MyChhild
{
public:
    MyChhildChild()
    {
        cout << "我是孙子 构造" << endl;
    }
public:
    virtual void print()  //2
    {
        cout << "我是孙子 working" << endl;
    }
protected:
private:
};


//搭建一个舞台 让对象唱戏  10:20
void howToPrintf(MyParent *base)
{
    base->print();    //多态的现象: 同一个调用语句 可以有多种形态 //1
    //扔过来一个子类对象 执行子类API函数 
    //扔过来一个父类对象 执行父类API函数 

    //C++编译器为含有虚函数的类的对象提前布局vptr指针和虚函数表 ;在发生多态的时候 (虚函数调用的时候), 去虚函数表中查找调用地址(函数的入口地址)
    //执行后来人写的代码
}

int main()
{

    MyParent   p1; //vptr指针  //虚函数表 
    MyChhild  c1;
    

    howToPrintf(&p1);
    howToPrintf(&c1);

    MyChhildChild   cc1;
    howToPrintf(&cc1);


    cout << "hello..." << endl;
    system("pause");
    return 0;
}
dm02_多态理解.cpp

多态理念场景图:

 

7、C++语言知识体系复习-C和C++横向比较

C和C++都是面向接口的。

 

8、C语言知识体系复习-回调函数

需求:用C语言的语法实现多态?

了解语法,练习:dm03_函数指针做函数参数.c

#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

//语法
//如何理解函数指针做函数参数(你是如何理解回调函数)
//语法层次上: 谁调用含有函数指针做函数参数的api函数 谁提供回调函数的入口地址
//调用关系上: 本来你去调用框架 ,结果 框架反过来调用你的API函数  ,所以叫回调
//好处:任务的调用者和任务的编写者解耦合

int   myAdd(int a, int b)
{
    int c = 0;
    c = a + b;
    return c;
}


int   myAdd2(int a, int b)
{
    int c = 0;
    c = a + b;
    return c;
}


int   myAdd3(int a, int b)
{
    int c = 0;
    c = a + b;
    return c;
}

//11:10
int   myAdd4(int a, int b)
{
    int c = 0;
    printf("func myAdd4() doing\n");
    c = a + b;
    
    return c;
}


//搭建平台 框架 能调用后来人写的代码 11:05
int MyFrameAdd(int (*myAddParam)(int a, int b),  int mya, int myb)
{
    int c = myAddParam(mya, myb);
    printf("%d, ", c);  //间接调用
    return 0;
}
int main()
{
    int mya = 10;
    int myb = 20;
    printf("直接调用 %d, ", myAdd(mya, myb));  //直接调用
    MyFrameAdd(myAdd4, 30, 40);
    printf("hello...\n");
    system("pause");
    return 0;
}
dm03_函数指针做函数参数.c

如何理解函数指针做函数参数(你是如何理解回调函数)?

语法层次上: 谁调用含有函数指针做函数参数的api函数 谁提供回调函数的入口地址
调用关系上: 本来你去调用框架 ,结果 框架反过来调用你的API函数  ,所以叫回调
好处:任务的调用者和任务的编写者解耦合

》回调函数模型:

 

9、C++面向抽象类编程思想回顾

》设计模式基本原则

  依赖倒置原则 (DIP,Dependence Inversion Principle)

依赖于抽象(接口),不要依赖具体的实现(类),也就是针对接口编程。

(1)

(2)

练习:dm04_面向抽象类编程.cpp

#include <iostream>
using namespace std;

class AbsMianBoard
{
public:
    virtual int doThing() = 0;

protected:
private:
};

class HSMainBoard : public AbsMianBoard
{
public:
    virtual int doThing()
    {
        cout << "华硕主板 工作...\n";
        return 0;
    }

protected:
private:
};

class AbsCpu
{
public:
    virtual int doThing() = 0;

protected:
private:
};

class ARMCpu : public AbsCpu
{
public:
    virtual int doThing()
    {
        cout << "ARMCpu  工作...\n";
        return 0;
    }

protected:
private:
};

//
class AbsHardDisk
{
public:
    virtual int doThing() = 0;

protected:
private:
};

class XSDisk : public AbsHardDisk
{
public:
    virtual int doThing()
    {
        cout << "XSDisk  工作...\n";
        return 0;
    }

protected:
private:
};


class Computer
{
public:
    Computer(AbsMianBoard    *absMainBoard,    AbsHardDisk    *absHardDisk,    AbsCpu    *absCpu)
    {
        m_absMainBoard = absMainBoard;
        m_absHardDisk = absHardDisk;
        m_absCpu = absCpu;
    }

    void doWork()
    {
        m_absMainBoard->doThing();
        m_absHardDisk->doThing();
        m_absCpu->doThing();

    }

protected:
private:
    AbsMianBoard    *m_absMainBoard;
    AbsHardDisk        *m_absHardDisk;
    AbsCpu            *m_absCpu;

};


int main()
{
    AbsMianBoard    *absMainBoard = new HSMainBoard;
    AbsHardDisk    *absHardDisk = new XSDisk;
    AbsCpu    *absCpu = new ARMCpu;


    Computer     myComputer(absMainBoard, absHardDisk, absCpu);
    myComputer.doWork();
    cout << "hello..." << endl;
    system("pause");
    return 0;
}
dm04_面向抽象类编程.cpp

 

三、安全传输平台项目扩展——keymngclient重构

1、项目需求和方案

需求回顾:

  app1、app2改动最小,最好是不改动
  保证密钥的安全分发
  网点的(对接入点做)生命周期的管理
  其他需求  性能  稳定

》需求和方案回顾:

 

2、C++类对象之间的关系-依赖和关联

类与类之间的关系对于理解面向对象具有很重要的作用,以前在面试的时候也经常被问到这个问题,在这里我就介绍一下。

》类与类之间存在以下关系:
(1)泛化(Generalization)
(2)关联(Association)
(3)依赖(Dependency)
(4)聚合(Aggregation)

(1)依赖(虚线): 一个类是另外一个类的函数参数或者函数返回值 //案例:张三开车

(2)关联(实线) 关联 张三 有车 一个类 是 另外一个类的成员变量 //案例:张三有车

(3)聚合(菱形实线) : 整体和部分的关系 汽车 发动机 (汽车可以选择各个型号的发动机)

(4)组合(实心形加实线) : 生命体,整体和部分的关系 汽车 发动机 (人 和 五脏六腑)

 

3、密钥协商客户端业务流复习

》SecMngclient和SecMngServer总体业务流设计

 

4、keymngclient设计与实现-思路

》secmngclient文件逻辑关系

 

5、keymngclient设计与实现-keymngclient的hello

(1)新建目录secmng,然后在secmng目录下拷贝makefile、新建inc目录和src目录,inc和src目录分别放置之前的头文件和源文件;

(2)修改*.c为*.cpp(如:修改keymngclient.c文件名为keymngclient.cpp);修改*.h中ifdef..endif更改为#pragma once;然后更改keymngclient.cpp输入输出(先只输出hello)和头文件;

(3)修改makefile(采用g++;目标文件改为.cpp;链接不需要的.o注释掉)

(4)测试

 

6、keymngclient设计与实现-LogHelper类

(1)修改keymnglog.h,增加LogHelper类,然后把keymnglog.cpp中的函数和全局变量添加到LogHelper类中,做成员变量和成员函数(增加static关键字);修改头文件;

(2)修改keymnglog.cpp,增加类的属性;

(3)修改keymngclient.cpp中调用LogHelper类中成员函数;

(4)修改makefile(增加链接的keymnglog.o);

(5)make编译,然后执行>./keymngclient 测试

 

7、keymngclient设计与实现-myipc类

(1)修改myipc_shm.h,增加MyIpc_ShmHelper类,然后把myipc_shm.cpp中的函数和全局变量添加到MyIpc_ShmHelper类中,做成员变量和成员函数(增加static关键字);修改头文件;

(2)修改myipc_shm.cpp,增加类的属性;

(3)修改makefile(增加链接的myipc_shm.o);

(4)make编译,然后执行>./keymngclient;测试

(5)报错void *转换到int损失精度,所以更改为:long myTmp = reinterpret_cast<int>(tempptr);

 

8、keymngclient设计与实现-KeyMng_ShmOp类

(1)修改keymng_shmop.h,增加KeyMng_ShmOp类,然后把keymng_shmop.cpp中的函数和全局变量添加到KeyMng_ShmOp类中,做成员变量和成员函数(增加static关键字);修改头文件;

(2)修改keymng_shmop.cpp,增加类的属性;

(3)修改makefile(增加链接的keymng_shmop.o);

(4)make编译,然后执行>./keymngclient;测试

(5)报错IPC_Creatshm、IPC_Mapshm、IPC_UnMapShm在此作用域中尚未声明,keymng_shmop.cpp中所有相应的IPC_xxx前面作用域加上MyIpc_ShmHelper:: 

(6)报错KyeMng_log在此作用域中尚未声明,keymng_shmop.cpp中所有的KyeMng_log前面作用域加上LogHelper:: 

(7)报错void *型指针用在了算术表达式中,所以更改为:pNode = reinterpret_cast<NodeSHMInfo *>(reinterpret_cast<long>(mapaddr)+sizeof(NodeSHMInfo)*i );

 

9、keymngclient设计与实现-应用程序框架类和业务流类设计思想

主框架增加:设计应用程序类KeyMngClientApp和业务流类KeyMngClientOp;所以在keymngclient.cpp中主函数修改为:

int main()
{
    cout << "hello keymngclient...\n";    
    /*
KeyMngClientApp keyMngClientApp; KeyMngClientOp keyMngClientOp;
//把业务流类对象 注入到 应用程序框架中 keyMngClientApp.setKeyMngClientOp(&keyMngClientOp); keyMngClientApp.init(); keyMngClientApp.run(); keyMngClientApp.exit(); */ return 0; }

 

10、keymngclient设计与实现-keymngclientapp

(1)拷贝keymngclient.cpp为keymngclientapp.cpp,拷贝keymngclient.cpp为keymngclientapp.h

(2)keymngclientapp.h删除除头文件其他部分,并注释掉socket相关的头文件,然后设计KeyMngClientApp类:

class KeyMngClientApp
{
public:
    KeyMngClientApp();
    ~KeyMngClientApp();
    
public:int init();
    int run();
    int exit();    
};

(3)keymngclientapp.cpp中注释掉socket相关的头文件,增加输入C++头文件,实现KeyMngClientApp类中的成员函数(内部实现待写,都先写return 0);

(4)修改makefile(增加链接的keymngclientapp.o);

(5)keymngclient.cpp增加头文件 #include "keymngclientapp.h",主函数更改声明;

(6)make编译,然后执行>./keymngclient;测试

 

11、keymngclient设计与实现-keymngclientop

(1)keymngclientop.h设计KeyMngClientOp类:

class KeyMngClientOp
{
public:
    //初始化客户端 全局变量
     int MngClient_InitInfo(MngClient_Info *pCltInfo);
    
     int MngClient_Quit(MngClient_Info *pCltInfo);
    
     int MngClient_Agree(MngClient_Info *pCltInfo);
    
     int MngClient_Check(MngClient_Info *pCltInfo);
    
     int MngClient_Revoke(MngClient_Info *pCltInfo);
    
     int MngClient_view(MngClient_Info *pCltInfo);    
};

(2)keymngclientop.cpp中注释掉socket相关的头文件,成员函数增加类的属性,成员函数(内部实现待修改,都只留return 0,其他都注释掉);

(3)修改makefile(增加链接的keymngclientop.o);

(4)make编译,然后执行>./keymngclient;测试

(5)keymngclientapp.h中KeyMngClientApp类中增加成员

class KeyMngClientApp
{
public:
    KeyMngClientApp();
    ~KeyMngClientApp();
    
public:
    int setKeyMngClientOp(KeyMngClientOp *keyMngClientOp);
    int init();
    int run();
    int exit();    
    
public:    
    KeyMngClientOp        *m_keyMngClientOp;
};

(6)keymngclientapp.cpp中实现成员函数setKeyMngClientOp

int KeyMngClientApp::setKeyMngClientOp(KeyMngClientOp *keyMngClientOp)
{
    m_keyMngClientOp =  keyMngClientOp;
    return 0;
}    

(7)keymngclient.cpp主函数更改声明和调用;

(8)make编译,然后执行>./keymngclient;测试

 

12、keymngclient设计与实现-初始化流程编写

keymngclientapp.cpp实现init函数:

int KeyMngClientApp::init()
{
    m_keyMngClientOp->MngClient_InitInfo(m_MngClientInfo);
    return 0;
}

由于MngClient_InitInfo(m_MngClientInfo)中使用到了m_MngClientInfo结构体,应该怎么分配内存呢?

(1)keymngclient.cpp中增加api函数

int main()
{
    cout << "hello keymngclient...\n";    
    KeyMngClientApp        keyMngClientApp;
    
    KeyMngClientOp        keyMngClientOp;
    
    MngClient_Info         mngClientInfo;
    
    
    //把业务流类对象 注入到 应用程序框架中
    keyMngClientApp.setKeyMngClientOp(&keyMngClientOp);
    keyMngClientApp.setMngClientInfo(&mngClientInfo);
    
    keyMngClientApp.init();
    keyMngClientApp.run();
    keyMngClientApp.exit();
            
    return 0;
}

 (2)keymngclientapp.h中增加成员函数和成员变量:

class KeyMngClientApp
{
public:
    KeyMngClientApp();
    ~KeyMngClientApp();
    
public:
    int setKeyMngClientOp(KeyMngClientOp *keyMngClientOp);
    int setMngClientInfo(MngClient_Info *mngClientInfo);
    int Usage();
    int init();
    int run();
    int exit();    
    
public:    
    KeyMngClientOp        *m_keyMngClientOp;
    MngClient_Info         *m_MngClientInfo;
};

 (3)keymngclientapp.cpp实现成员函数setMngClientInfo;

int KeyMngClientApp::setMngClientInfo(MngClient_Info *mngClientInfo)
{
    m_MngClientInfo =  mngClientInfo;
    return 0;
}    

(4)keymngclientop.cpp更改MngClient_InitInfo函数中调用的函数的类作用域;

(5)make编译,然后执行>./keymngclient;测试

 

13、keymngclient设计与实现-初始化流程调试

>gdb keymngclient

调试后,在keymngclient.cpp中增加返回值判断

int main()
{
    int         ret = 0;
    cout << "hello keymngclient...\n";    
    KeyMngClientApp        keyMngClientApp;
    
    KeyMngClientOp        keyMngClientOp;
    
    MngClient_Info         mngClientInfo;
    
    
    //把业务流类对象 注入到 应用程序框架中
    keyMngClientApp.setKeyMngClientOp(&keyMngClientOp);
    keyMngClientApp.setMngClientInfo(&mngClientInfo);
    
    ret = keyMngClientApp.init();
    if (ret != 0)
    {
        printf("keymngclient init err:%d \n", ret);    
        return ret;
    }
    printf("keymngclient init ok \n");    
    
    keyMngClientApp.run();
    keyMngClientApp.exit();
    
    
                    
    return 0;
}

 

14、keymngclient设计与实现-密钥协商编写和调试

(1)keymngclientapp.cpp中实现成员函数Usage()、run();

(2)keymngclientop.cpp中打开之前注释的密钥协商的函数MngClient_Agree(增加里边调用的函数的作用域,先注释掉日志调用);

(3)make编译,然后执行>./keymngclient;测试

(4)打开socket函数和头文件;

(5)报错:poolsocket.h:5:错误:expected unqualified-id before 'C';poolsocket.h中第5行更改为extern "C"(注意:在linux下gcc中extern 'C'单引号没有问题,但是g++单引号会报错,需要更改为双引号extern "C"!!!)

(6)打开另一个终端,启动原来C编写的服务器后台>./keymngserver,然后再原终端执行>./keymngclient;测试

 

 

在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

posted on 2020-08-03 15:49  Alliswell_WP  阅读(380)  评论(0编辑  收藏  举报

导航