在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主: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... "); //获取文件内容 ret = getFileContent(myFileName, &myP, &myLine); if (ret != 0) { printf("func getFileContent() err:%d ", ret); return ret; } //打印文件内容 按照行 for (i = 0; i < myLine; i++) { printf("%s ", myP[i]); } //getFileContent_Free(myP, myLine); getFileContent_Free2(&myP, myLine); system("pause"); return 0; }
问题:为什么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; }
多态理念场景图:
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 "); 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... "); system("pause"); return 0; }
》如何理解函数指针做函数参数(你是如何理解回调函数)?
语法层次上: 谁调用含有函数指针做函数参数的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 << "华硕主板 工作... "; return 0; } protected: private: }; class AbsCpu { public: virtual int doThing() = 0; protected: private: }; class ARMCpu : public AbsCpu { public: virtual int doThing() { cout << "ARMCpu 工作... "; return 0; } protected: private: }; // class AbsHardDisk { public: virtual int doThing() = 0; protected: private: }; class XSDisk : public AbsHardDisk { public: virtual int doThing() { cout << "XSDisk 工作... "; 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; }
三、安全传输平台项目扩展——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... "; /*
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... "; 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... "; KeyMngClientApp keyMngClientApp; KeyMngClientOp keyMngClientOp; MngClient_Info mngClientInfo; //把业务流类对象 注入到 应用程序框架中 keyMngClientApp.setKeyMngClientOp(&keyMngClientOp); keyMngClientApp.setMngClientInfo(&mngClientInfo); ret = keyMngClientApp.init(); if (ret != 0) { printf("keymngclient init err:%d ", ret); return ret; } printf("keymngclient init ok "); 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,转载请注明出处。