C++学习之外联接口-项目总结
目录
1.知识点概述
2.秘钥协商的秘钥写入到共享内存中
3.秘钥写共享内存测试
4.外联接口作用
5.外联接口类的封装
6.配置文件定义
7.外联接口的打包
8.外联接口的实现
9.外联接口读共享内存得到秘钥
10.将外联接口制作成动态库
11.外联接口的使用
12.外联接口测试-有bug
13.外联接口测试
14.外联接口的改进
15.知识点总结
16.项目中知识点总结
17.线程池思想
18.ora-24550解决方案
19.秘钥注销和校验
1.知识点概述
> 为了更容易(同时也更安全的管)的使用动态内存,新的标准库(C++11)提供了两种智能指针(smart pointer)类型来管理动态对象。智能指针的行为类似于常规指针。重要的区别是它负责自动释放所指向的对象。新标准提供的这两种智能指针的区别在于管理底层指针的方式:shared_ptr允许多个指针指向同一个对象;unique_ptr则独占所指向的对象。
>
> week_ptr -> 管理shared_ptr
>
> auto_ptr -> c++17
>
> **智能指针也是模板。**
- 头文件
```c++
#include <memory>
```
- 一般格式
```c
shared_ptr<string> p1; //指向string的shared_ptr
shared_ptr<int> p2; //指向int的shared_ptr
2.秘钥协商的秘钥写入到共享内存中
```c
int * a = new int(100);
// 使用智能指针和一块内存进行管联, 这块内存就有一个引用计数, 关联一次引用计数+1
// 当内存引用计数减为 0 时候, 内存就被指针指针删除了
```
> **shared_ptr 允许有多个指针指向同一个对象**, **我们可以认为每个share_ptr都有一个关联的计数器,通常称为引用计数(reference count)。无论何时我们拷贝一个shared_ptr,计数器都会递增。**<font color="red">一旦某个对象的引用计数变为0,这个对象会被自动删除。</font>
>
> 例如这些都会使所关联的计数器**递增**:
>
> **(1)当用一个shared_ptr初始化另外一个shared_ptr**
>
> **(2)将它作为参数传递给另外一个函数**
>
> **(3)作为函数的返回值**
3.秘钥写共享内存测试
> 例如这些操作会使计数器都会**递减:**
>
> **(1)给share_ptr赋新的值**
>
> **(2)share_ptr被销毁**
>
> **(3)局部的share_ptr离开作用域**
- 初始化
```c
// 通过 shared_ptr 的构造函数,可以让 shared_ptr 对象托管一个 new 运算符返回的指针
shared_ptr<T> ptr(new T); // T 可以是 int、char、类等各种类型
// 使用make_shared函数分配一个对象并初始化它,make_shared函数返回一个指向此对象的shared_ptr
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
// 通过另一个智能指针初始化
shared_ptr<T> ptr = ptr1;
shared_ptr<T> ptr(ptr1);
4.外联接口作用
查看引用计数
```c++
long use_count() const noexcept;
```
- 返回存储的指针
```c++
T* get() const noexcept;
```
- 重置管理的指针
```c++
void reset( Y* ptr );
```
5.外联接口类的封装
- 删除器 -> 回调函数 -> 删除智能指针管理的内存
```c
// 默认, 删除器函数, 会自动调用
default_delete()
shared_ptr<int> ptr(new int(2), default_delete<int>());
// share_ptr 的 默认删除器, 不能删除new的数组内存, 需要自己指定删除器函数
// 通过share_ptr的构造函数, 在构造函数的第二个参数的位置上, 指定回调函数
// 匿名函数
[](args)
{
}
6.配置文件定义
shared_ptr<int> ptr1(new int[10], [](int *array) {
delete[]array;
});
shared_ptr<A> ptr2(new A[10], [](A* a) {
delete []a;
});
// 规律:
删除器函数需要一个参数, 参数类型: 智能指针管理的内存地址的类型, 函数体中进行内存释放
删除器函数别智能指针调用的时候, 会将new得到的内存地址, 传递给删除器函数的参数
```
7.外联接口的打包
> **unique_ptr独占所指向的对象。**同一时刻只能有一个 unique_ptr 指向给定对象(通过禁止拷贝语义, 只有移动语义来实现)
- 初始化
```c
// 使用构造函数
unique_ptr<T> ptr(new T); // T 可以是 int、char、类等各种类型
// 使用make_unique
template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args );
```
- unique_ptr 支持对数组内存管理
8.外联接口的实现
# 2. 外联接口
1. 接口类的封装
```c++
// 读共享内存中的秘钥, 进行对称加密
// des , 3des , aes
class MyInterface
{
public:
// json参数磁盘的json格式的配置文件
MyInterface(string json);
~MyInterface();
// 数据加密
// 参数: 待加密的数据->明文, 返回值: 密文
string encryptData_des(string str);
string encryptData_3des(string str);
string encryptData_aes(string str);
// 数据解密
// 参数: 待解密的数据-密文, 返回值: 明文
string decryptData_des(string str);
string decryptData_3des(string str);
string decryptData_aes(string str);
}
9.外联接口读共享内存得到秘钥
10.将外联接口制作成动态库
2. 提供的接口不是一个应用程序 -> 不是进程
- 如果从共享内存中读数据?
- 虽然接口不是程序, 但是要被业务程序调用
- 业务程序是进程
- 通过业务程序完成进程间通信
- 外联接口要求必须要通用
- 必须用通过配置文件读配置信息 -> 找到共享内存
11.外联接口的使用
3. json格式配置文件的定义
```json
// 共享内存中存储的节点结构
class NodeSecKeyInfo
{
public:
NodeSecKeyInfo() : status(0), seckeyID(0)
{
bzero(clientID, sizeof(clientID));
bzero(serverID, sizeof(serverID));
bzero(seckey, sizeof(seckey));
}
int status; // 秘钥状态: 1可用, 0:不可用
int seckeyID; // 秘钥的编号
char clientID[12]; // 客户端ID, 客户端的标识
char serverID[12]; // 服务器ID, 服务器标识
char seckey[128]; // 对称加密的秘钥
};
12.外联接口测试-有bug
```json
// 服务器端, 必须能找到对应的秘钥
{
"ShmKey":"/usr/lib", // 通过 shmKey 打开一块已经存在的共享内存
"MaxNode":100, // 共享内存中存储的最大节点数 -> 用于遍历
"ServerID":"999", // 当前秘钥协商服务器的ID
"ClientID":"666" // 和当前业务服务器通信的客户端ID --> 这个是动态的
}
// 客户端
{
"shmKey":"/usr/local", // 通过 shmKey 打开一块已经存在的共享内存
"MaxNode":1, // 共享内存中存储的最大节点数 -> 用于遍历
"ServerID":"999",
"ClientID":"666"
}
13.外联接口测试
```json
// 改进配置文件
// 将配置文件中的serverID和clientID去掉
// 找秘钥的方式:
- 通过clientID和serverID进行查找
- 通过秘钥ID查找
// 通过配置文件打开共享内存
{
"shmKey":"/usr/local", // 通过 shmKey 打开一块已经存在的共享内存
"MaxNode":1, // 共享内存中存储的最大节点数 -> 用于遍历
}
14.外联接口的改进
```c++
// 共享内存类修改
class SecKeyShm : public BaseShm
{
public:
// 打开或创建一块共享内存
// 这个操作是在父类中做的
SecKeyShm(int key, int maxNode);
SecKeyShm(string pathName, int maxNode);
~SecKeyShm();
void shmInit();
int shmWrite(NodeSecKeyInfo* pNodeInfo);
NodeSecKeyInfo shmRead(string clientID, string serverID);
NodeSecKeyInfo shmRead(int keyID);
// 通过这个函数读共享内存中的第一个NodeSecKeyInfo
// 给客户端使用
NodeSecKeyInfo shmFirstNode();
private:
int m_maxNode;
};
```
15.知识点总结
4. 如何给用户使用?
- 将接口打包成库(静态/动态)
- 静态库
```c
// 生成.o
gcc/g++ *.c/*.cpp -c
// 打包.o
ar rcs libxxx.a *.o
```
- 动态库
```shell
# 生成.o -> 和位置无关使用的是相对地址
gcc/g++ *.c/*.cpp -c -fpic
# 生成动态库
gcc/g++ -shared *.o -o libxxx.so
```
16.项目中知识点总结
- 发布对应的头文件即可
# 3.其他操作
## 3.1 OCCI - ORA-24550
在使用occi多线程访问oralce服务器的时候, 会出现ORA-24550 错误, 错误信息如下:
```shell
ORA-24550 : signal received : [si_signo=11] [si_errno=0] [si_code=50] [si_adr =
2020202020202020] killed
17.线程池思想
18.ora-24550解决方案
该错误会导致进程终止, 修改方案如下:
1. 使用find命令所有oracle服务器端的 `sqlnet.ora` 文件, 在文件中添加下配置项:
```shell
DIAG_ADR_ENABLED=OFF
DIAG_SIGHANDLER_ENABLED=FALSE
DIAG_DDE_ENABLED=FALSE
```
2. 如果该问题还未解决, 在调用 `OCCI` 接口的客户端对应oracle目录中, 例如, 我的客户端对用的oralce目录为
`/opt/instantclient_11_2` , 在该目录下的 `network/admin` 中添加文件 `sqlnet.ora` , 内容如下:
```shell
SQLNET.AUTHENTICATION_SERVICES= (NTS)
NAMES.DIRECTORY_PATH= (TNSNAMES,HOSTNAME)
DIAG_ADR_ENABLED=OFF
DIAG_SIGHANDLER_ENABLED=FALSE
DIAG_DDE_ENABLED=FALSE
19.秘钥注销和校验
## 3.2 秘钥校验
对比通信双方使用的秘钥是否相同
- 如何比较?
- 在客户端将秘钥进行哈希计算 -> 哈希值
- 将哈希值发送给服务器
- 服务器端也将秘钥生成哈希值
- 两个哈希值进行对比即可
## 3.3 秘钥注销
- 现有的共享内存中的秘钥不用了, 标记为不可用
- 客户端:
- 将本地共享内存中的秘钥状态进行修改
- 通知服务器这个秘钥不用了
- 将这个秘钥的ID发送给服务器
- 服务器端:
- 通过秘钥的ID找到共享内存中的秘钥, 标记为不可用
- 更新数据库中这个秘钥的状态