文章目录
C++ - 多个buffer合并成一个buffer的管理类
概述
客户端要向服务端提交包含在多个buffer中的信息, 如果提交多个buffer(文件), 挺麻烦的.
如果将这些buffer(文件), 压缩成一个文件, 处理起来也不是很方便.
尝试将多个buffer封装到一个buffer中, 最终将封装好的buffer(写成文件也行)给服务端, 只需要提交一次.
封装了一个管理类, 可以向管理类push多个buffer, 最后调用接口, 得到一个完整的大buffer(包含所有压入的小buffer).
对提取的大buffer, 使用者的程序, 再调用分析接口, 可以取得调用者压入的多个小buffer的具体信息.
笔记
运行效果
header info :
name = myapp_cfg
buffer total len = 265
buffer block cnt = 3
----------------
dataBlock[0] = hd_info, ver = 0X1000100, datalen = 40, data below
0000 - 31 2e 20 77 65 20 67 65-74 20 73 6f 6d 65 20 68 1. we get some h
0010 - 61 72 64 77 61 72 65 20-69 6e 66 6f 20 66 72 6f ardware info fro
0020 - 6d 20 63 6c 69 65 6e 74- m client
----------------
dataBlock[1] = soft_info, ver = 0X1000100, datalen = 40, data below
0000 - 32 2e 20 77 65 20 67 65-74 20 73 6f 6d 65 20 73 2. we get some s
0010 - 6f 66 74 77 61 72 65 20-69 6e 66 6f 20 66 72 6f oftware info fro
0020 - 6d 20 63 6c 69 65 6e 74- m client
----------------
dataBlock[2] = stuff_info, ver = 0X1010001, datalen = 37, data below
0000 - 32 2e 20 77 65 20 67 65-74 20 73 6f 6d 65 20 73 2. we get some s
0010 - 74 75 66 66 20 69 6e 66-6f 20 66 72 6f 6d 20 63 tuff info from c
0020 - 6c 69 65 6e 74 lient
free map, g_mem_hook_map.size() = 0
测试工程实现
main.cpp
/*!
* \file main.cpp
*/
#include "my_openSSL_lib.h"
#include <openssl/crypto.h>
#include <openssl/bio.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "CMemHookRec.h"
#include "CMyDataContainerBase.h"
bool make_DataBuffer(uint8_t*& pBuf, int& lenBuf);
bool use_DataBuffer(uint8_t*& pBuf, int& lenBuf);
void my_openssl_app();
int main(int argc, char** argv)
{
setvbuf(stdout, NULL, _IONBF, 0); // 清掉stdout缓存, 防止调用printf时阻塞
mem_hook();
my_openssl_app();
mem_unhook();
/*! run result
header info :
name = myapp_cfg
buffer total len = 265
buffer block cnt = 3
----------------
dataBlock[0] = hd_info, ver = 0X1000100, datalen = 40, data below
0000 - 31 2e 20 77 65 20 67 65-74 20 73 6f 6d 65 20 68 1. we get some h
0010 - 61 72 64 77 61 72 65 20-69 6e 66 6f 20 66 72 6f ardware info fro
0020 - 6d 20 63 6c 69 65 6e 74- m client
----------------
dataBlock[1] = soft_info, ver = 0X1000100, datalen = 40, data below
0000 - 32 2e 20 77 65 20 67 65-74 20 73 6f 6d 65 20 73 2. we get some s
0010 - 6f 66 74 77 61 72 65 20-69 6e 66 6f 20 66 72 6f oftware info fro
0020 - 6d 20 63 6c 69 65 6e 74- m client
----------------
dataBlock[2] = stuff_info, ver = 0X1010001, datalen = 37, data below
0000 - 32 2e 20 77 65 20 67 65-74 20 73 6f 6d 65 20 73 2. we get some s
0010 - 74 75 66 66 20 69 6e 66-6f 20 66 72 6f 6d 20 63 tuff info from c
0020 - 6c 69 65 6e 74 lient
free map, g_mem_hook_map.size() = 0
*/
return 0;
}
void my_openssl_app()
{
uint8_t* pBuf = NULL;
int lenBuf = 0;
make_DataBuffer(pBuf, lenBuf);
use_DataBuffer(pBuf, lenBuf);
if (NULL != pBuf)
{
delete[] pBuf;
pBuf = NULL;
}
}
bool make_DataBuffer(uint8_t*& pBuf, int& lenBuf)
{
CMyDataContainerBase* pDataContainer = NULL;
bool b_rc = false;
char szBuf[1024];
do {
pDataContainer = new CMyDataContainerBase();
if (NULL == pDataContainer)
{
break;
}
// my app cfg 1.0.0.1
pDataContainer->SetHeader("myapp_cfg", 0x01000001);
// add data block - hdinfo 1.0.1.0
strcpy(szBuf, "1. we get some hardware info from client");
pDataContainer->addDataBuffer("hd_info", 0x01000100, (uint8_t*)szBuf, strlen(szBuf));
// add data block - soft info 1.0.1.0
strcpy(szBuf, "2. we get some software info from client");
pDataContainer->addDataBuffer("soft_info", 0x01000100, (uint8_t*)szBuf, strlen(szBuf));
// add data block - stuff info 1.1.0.1
strcpy(szBuf, "2. we get some stuff info from client");
pDataContainer->addDataBuffer("stuff_info", 0x01010001, (uint8_t*)szBuf, strlen(szBuf));
if (!pDataContainer->GetTotalBuffer(pBuf, lenBuf))
{
assert(false);
break;
}
b_rc = true;
} while (false);
if (NULL != pDataContainer)
{
delete pDataContainer;
pDataContainer = NULL;
}
return b_rc;
}
bool use_DataBuffer(uint8_t*& pBuf, int& lenBuf)
{
bool b_rc = false;
CMyDataContainerBase* pDataContainer = NULL;
TAG_DATA_CONTAINER_HEADER* pheader = NULL;
TAG_DATA_BUFFER* pDataBuffer = NULL;
int i = 0;
do {
pDataContainer = new CMyDataContainerBase();
if (NULL == pDataContainer)
{
assert(false);
break;
}
if (!pDataContainer->loadBuffer(pBuf, lenBuf))
{
assert(false);
break;
}
if (!pDataContainer->getHeader(pheader))
{
assert(false);
break;
}
printf("header info :\n");
printf("name = %s\n", (char*)pheader->name_8d3);
printf("buffer total len = %d\n", pheader->totoal_len);
printf("buffer block cnt = %d\n", pheader->databuffer_cnt);
for (i = 0; i < pheader->databuffer_cnt; i++)
{
printf("----------------\n");
if (!pDataContainer->getDataBuffer(i, pDataBuffer))
{
assert(false);
goto label_err;
}
printf("dataBlock[%d] = %s, ver = 0X%X, datalen = %d, data below\n", i, (char*)pDataBuffer->name_8d3, pDataBuffer->ver, pDataBuffer->len_data);
BIO_dump_fp(stdout, pDataBuffer->pdata, pDataBuffer->len_data);
}
b_rc = true;
} while (false);
label_err:
return b_rc;
}
CMyDataContainerBase.h
//! \file CMyDataContainerBase.h
//! \note 将多个指针存到buffer中, 接收者将多个指针从数据块中提取出来用
#include <cstdint>
#include <list>
#ifndef __CMYDATACONTAINERBASE_H__
#define __CMYDATACONTAINERBASE_H__
// 数据容器形成buffer(文件)之后的格式
// TAG_DATA_CONTAINER_HEADER[1]
// TAG_DATA_BUFFER[n]
// data_block[n]
typedef struct _tag_data_container_header
{
uint8_t name_8d3[13];
int totoal_len; // 数据容器文件的大小, 从文件开头算起, 到文件结尾
int databuffer_cnt;
uint8_t hash[32];
}TAG_DATA_CONTAINER_HEADER;
typedef struct _tag_data_buffer
{
uint8_t name_32[33];
uint8_t* pdata;
int posOnTotalMem; // 在数据文件中的位置, 从数据文件开头算起
int len_data;
_tag_data_buffer()
{
memset(name_32, 0, sizeof(name_32));
pdata = NULL;
posOnTotalMem = 0;
len_data = 0;
}
}TAG_DATA_BUFFER;
class CMyDataContainerBase
{
public:
CMyDataContainerBase();
virtual ~CMyDataContainerBase();
void clear();
// 组装者用的接口
void SetHeader(const char* pszName8d3);
void addDataBuffer(const char* pszName8d3, uint8_t* pdata, int len_data); // pdata内容会复制到类内部, 和外部指针无关
// 如果要对数据做附加处理, 可以继承此类, 先调用父类接口实现, 再对数据做其他处理(e.g. encrypt/encode)
// 只是更新了pBuf中的header中的hash值!!!
bool update_hash(uint8_t*& pBuf, int& lenBuf); // 此时,应是数据都加完了,调用了GetTotalBuffer(), 然后送进update()来更新header中的hash
virtual bool GetTotalBuffer(uint8_t*& pBuf, int& lenBuf); // 得到的pBuf需要调用者自己释放
// 接受者用的接口
// 如果要对数据做附加处理, 可以继承此类, 先对数据做其他处理(e.g. decrypt/decode), 再调用父类接口
virtual bool loadBuffer(uint8_t* pBuf, int lenBuf);
bool getHeader(TAG_DATA_CONTAINER_HEADER*& header);
bool getDataBuffer(int index, TAG_DATA_BUFFER*& dataBuf);
private:
TAG_DATA_CONTAINER_HEADER m_header;
std::list<TAG_DATA_BUFFER*> m_list;
};
#endif // #ifndef __CMYDATACONTAINERBASE_H__
CMyDataContainerBase.cpp
//! \file CMyDataContainerBase.cpp
#include "pch.h"
#include "MemOpt/CMyDataContainerBase.h"
#include <cassert>
#include "openssl/evp.h"
#include "memOpt/my_debug_new_define.h"
#include <cipher/cipher_sha3_512.h>
CMyDataContainerBase::CMyDataContainerBase()
{
clear();
}
CMyDataContainerBase::~CMyDataContainerBase()
{
clear();
}
void CMyDataContainerBase::clear()
{
std::list<TAG_DATA_BUFFER*>::iterator it;
TAG_DATA_BUFFER* dataBuffer = NULL;
memset(&m_header, 0, sizeof(m_header));
for (it = m_list.begin(); it != m_list.end(); it++)
{
dataBuffer = *it;
if (NULL != dataBuffer)
{
if (NULL != dataBuffer->pdata)
{
delete[] dataBuffer->pdata;
dataBuffer->pdata = NULL;
}
// 这是我今天上午修正的内存泄漏
delete dataBuffer;
dataBuffer = NULL;
}
}
m_list.clear();
}
void CMyDataContainerBase::SetHeader(const char* pszName8d3)
{
do {
if ((NULL == pszName8d3) || (strlen(pszName8d3) > sizeof(m_header.name_8d3)))
{
assert(false);
break;
}
strcpy((char*)m_header.name_8d3, pszName8d3);
} while (false);
}
void CMyDataContainerBase::addDataBuffer(const char* pszName8d3, uint8_t* pdata, int len_data)
{
TAG_DATA_BUFFER* pDataBuffer = NULL;
int len = 0;
do {
if (NULL != pszName8d3)
{
len = (int)strlen(pszName8d3);
}
if ((NULL == pszName8d3) || (strlen(pszName8d3) >= sizeof(TAG_DATA_BUFFER::name_32)))
{
assert(false);
break;
}
if ((NULL == pdata) || (len_data <= 0))
{
assert(false);
break;
}
pDataBuffer = new TAG_DATA_BUFFER;
if (NULL == pDataBuffer)
{
assert(false);
break;
}
memset(pDataBuffer, 0, sizeof(TAG_DATA_BUFFER));
strcpy((char*)pDataBuffer->name_32, pszName8d3);
pDataBuffer->posOnTotalMem = 0;
pDataBuffer->len_data = len_data;
pDataBuffer->pdata = new uint8_t[pDataBuffer->len_data];
if (NULL == pDataBuffer->pdata)
{
assert(false);
break;
}
memcpy(pDataBuffer->pdata, pdata, pDataBuffer->len_data);
m_list.push_back(pDataBuffer);
} while (false);
}
bool CMyDataContainerBase::update_hash(uint8_t*& pBuf, int& lenBuf)
{
bool b_rc = false;
bool b = false;
TAG_DATA_CONTAINER_HEADER* pHeader = NULL;
CCipherSha3_256 hash;
do {
if ((NULL == pBuf) || (lenBuf <= 0))
{
break;
}
pHeader = (TAG_DATA_CONTAINER_HEADER*)(pBuf + 0);
memset(pHeader->hash, 0, sizeof(pHeader->hash));
b = hash.begin();
assert(true == b);
hash.update(pBuf, lenBuf);
assert(true == b);
hash.end();
assert(true == b);
assert(hash.get_md_len() == sizeof(pHeader->hash));
memcpy(pHeader->hash, hash.get_md(), hash.get_md_len());
b_rc = true;
} while (false);
return b_rc;
}
bool CMyDataContainerBase::GetTotalBuffer(uint8_t*& pBuf, int& lenBuf)
{
bool b_rc = false;
pBuf = NULL;
lenBuf = 0;
int szList = 0;
std::list<TAG_DATA_BUFFER*>::iterator it;
TAG_DATA_CONTAINER_HEADER* pHeader = NULL;
TAG_DATA_BUFFER* pDataBuffer = NULL;
TAG_DATA_BUFFER* pDataBufferMem = NULL;
uint8_t* pDataBufferMemData = NULL; // pBuf用的
int i = 0;
int lenDataTotalNow = 0;
do {
// 数据容器形成buffer(文件)之后的格式
// TAG_DATA_CONTAINER_HEADER[1]
// TAG_DATA_BUFFER[n]
// data_block[n]
szList = (int)m_list.size();
lenBuf = sizeof(TAG_DATA_CONTAINER_HEADER) + szList * sizeof(TAG_DATA_BUFFER);
lenDataTotalNow = 0;
for (i = 0, it = m_list.begin(); it != m_list.end(); it++, i++)
{
pDataBuffer = *it;
if (NULL == pDataBuffer)
{
assert(false);
goto label_err;
}
lenBuf += pDataBuffer->len_data;
pDataBuffer->posOnTotalMem = (int)(sizeof(TAG_DATA_CONTAINER_HEADER) + szList * sizeof(TAG_DATA_BUFFER) + lenDataTotalNow);
lenDataTotalNow += pDataBuffer->len_data;
}
pBuf = (uint8_t*)OPENSSL_malloc(lenBuf);
if (NULL == pBuf)
{
assert(false);
break;
}
memset(pBuf, 0, lenBuf);
// 填充header;
m_header.databuffer_cnt = szList;
m_header.totoal_len = lenBuf;
memset(m_header.hash, 0, sizeof(m_header.hash));
// copy header
pHeader = (TAG_DATA_CONTAINER_HEADER*)(pBuf + 0);
memcpy(pHeader, &m_header, sizeof(m_header));
// 依次填充DataBuffer
for (i= 0, it = m_list.begin(); it != m_list.end(); it++, i++)
{
pDataBuffer = *it;
pDataBufferMem = (TAG_DATA_BUFFER*)(pBuf + sizeof(TAG_DATA_CONTAINER_HEADER) + i * sizeof(TAG_DATA_BUFFER));
memcpy(pDataBufferMem, pDataBuffer, sizeof(TAG_DATA_BUFFER));
pDataBufferMem->pdata = NULL; // 放入buffer之后,pdata就没用了,而且会影响算hash.
pDataBufferMemData = (pBuf + pDataBuffer->posOnTotalMem);
memcpy(pDataBufferMemData, pDataBuffer->pdata, pDataBuffer->len_data);
}
b_rc = true;
} while (false);
label_err:
return b_rc;
}
bool CMyDataContainerBase::loadBuffer(uint8_t* pBuf, int lenBuf)
{
bool b_rc = false;
TAG_DATA_CONTAINER_HEADER* pHeader = NULL;
TAG_DATA_BUFFER* pDataBuf = NULL;
int iTotalLenNow = 0;
int i = 0;
do {
if ((NULL == pBuf) || (lenBuf <= 0))
{
break;
}
clear();
// 数据容器形成buffer(文件)之后的格式
// TAG_DATA_CONTAINER_HEADER[1]
// TAG_DATA_BUFFER[n]
// data_block[n]
iTotalLenNow += sizeof(TAG_DATA_CONTAINER_HEADER);
if (iTotalLenNow > lenBuf)
{
break;
}
pHeader = (TAG_DATA_CONTAINER_HEADER*)pBuf;
iTotalLenNow += (pHeader->databuffer_cnt * sizeof(TAG_DATA_BUFFER));
if (iTotalLenNow > lenBuf)
{
break;
}
for (i = 0; i < pHeader->databuffer_cnt; i++)
{
pDataBuf = (TAG_DATA_BUFFER*)(pBuf + sizeof(TAG_DATA_CONTAINER_HEADER) + sizeof(TAG_DATA_BUFFER));
if ((pDataBuf->posOnTotalMem + pDataBuf->len_data) > lenBuf)
{
goto label_err;
}
}
// 到此, 数据格式基本正确
// 将数据载入 m_header, m_list
memcpy(&m_header, (pBuf + 0), sizeof(TAG_DATA_CONTAINER_HEADER));
for (i = 0; i < pHeader->databuffer_cnt; i++)
{
pDataBuf = new TAG_DATA_BUFFER;
if (NULL == pDataBuf)
{
assert(false);
goto label_err;
}
memcpy(pDataBuf, (pBuf + sizeof(TAG_DATA_CONTAINER_HEADER) + i * sizeof(TAG_DATA_BUFFER)), sizeof(TAG_DATA_BUFFER));
pDataBuf->pdata = new uint8_t[pDataBuf->len_data];
if (NULL == pDataBuf->pdata)
{
assert(false);
goto label_err;
}
memcpy(pDataBuf->pdata, pBuf + pDataBuf->posOnTotalMem, pDataBuf->len_data);
m_list.push_back(pDataBuf);
}
b_rc = true;
} while (false);
label_err:
return b_rc;
}
bool CMyDataContainerBase::getHeader(TAG_DATA_CONTAINER_HEADER*& header)
{
header = &m_header;
return true;
}
bool CMyDataContainerBase::getDataBuffer(int index, TAG_DATA_BUFFER*& dataBuf)
{
bool b_rc = false;
std::list<TAG_DATA_BUFFER*>::iterator it;
int i = 0;
TAG_DATA_BUFFER* pDataBuffer = NULL;
do {
if (index >= this->m_header.databuffer_cnt)
{
break;
}
for (i = 0, it = m_list.begin(); it != m_list.end(); it++, i++)
{
if (i != index)
{
continue;
}
pDataBuffer = *it;
if (NULL == pDataBuffer)
{
assert(false);
goto label_err;
}
dataBuf = pDataBuffer;
b_rc = true;
break;
}
} while (false);
label_err:
return b_rc;
}
备注
如果不想用户直接能分析提交的内容(因为buffer的内容有可能包含ascii字符, 人眼可以分辨), 现在能想到的方法有2种:
- 在执行GetTotalBuffer()后, 得到总的buffer, 对buffer做编解码/加解密处理
- 直接从CMyDataContainerBase继承一个子类, 重载GetTatalBuffer()/loadBuffer(), 对提交/载入的数据在子类中直接处理.