数据压缩是将原有数据通过某种压缩算法计算得到相对数据量小的过程。这种过程是可逆的,即能通过压缩后的数据恢复出原数据。数据压缩能够节省存储空间,减轻网络负载。在即需要加密又需要压缩的情况下,必须先压缩再加密,次序不能颠倒。因为加密后的数据是一个无序的数据,对它进行数据压缩,效果不大。SSL协议本身支持压缩算法,OpenSSL实现也支持压缩算法。它实现了一个空的压缩算法(crypto/comp/c_rle.c)并支持zlib压缩算法(crypto/comp/c_zlib.c)。OpenSSL中用户可实现自己的压缩算法。
本文假设你已经安装好了OpenSSL,并且持有一份1.1.1的源码。
压缩相关的头文件在comp.h中、源文件在crypto/comp目录中。
主要结构:
struct comp_method_st {
int type; /* NID for compression library */
const char *name; /* A text string to identify the library */
int (*init) (COMP_CTX *ctx);
void (*finish) (COMP_CTX *ctx);
int (*compress) (COMP_CTX *ctx,
unsigned char *out, unsigned int olen,
unsigned char *in, unsigned int ilen);
int (*expand) (COMP_CTX *ctx,
unsigned char *out, unsigned int olen,
unsigned char *in, unsigned int ilen);
};
typedef struct comp_method_st COMP_METHOD;
这个结构定义了压缩的抽象方法。相关字段含义:
type —— 压缩库的NID定义,如NID_zlib_compression。
name —— 压缩库的名称。
init —— 指定初使化方法。
finish —— 指定结束方法。
compress —— 指定压缩方法。
expand —— 指定解压缩方法。
struct comp_ctx_st {
struct comp_method_st *meth;
unsigned long compress_in;
unsigned long compress_out;
unsigned long expand_in;
unsigned long expand_out;
void* data;
};
typedef struct comp_ctx_st COMP_CTX;
这个结构定义了压缩的上下文结构。相关字段含义:
meth —— 关联的压缩抽象方法。
compress_in —— 被压缩数据总字节数。
compress_out —— 压缩数据(结果)总字节数。
expand_in —— 被解压数据总字节数。
expand_out —— 解压数据(结果)总字节数。
data —— 供用户使用的扩展数据,用于存放用户自定义的信息。
在1.1.1中,大多数的数据结构已经不再向使用者开放,从封装的角度来看,这是更合理的。如果你在头文件中找不到结构定义,不妨去源码中搜一搜。
主要函数:
COMP_METHOD *COMP_zlib(void);
这个函数用于创建zlib压缩抽象方法。必须要求系统上安装有zlib库。
COMP_CTX *COMP_CTX_new(COMP_METHOD *meth);
指定压缩抽象方法,创建压缩上下文。
注意:meth不能传NULL,OpenSSL的内部实现并没有处理在传入NULL的情况下做默认处理。
void COMP_CTX_free(COMP_CTX *ctx);
释放压缩上下文。
int COMP_CTX_get_type(const COMP_CTX* ctx);
从压缩上下文获取压缩库的NID。
const COMP_METHOD *COMP_CTX_get_method(const COMP_CTX *ctx);
从压缩上下文获取压缩抽象方法。
int COMP_get_type(const COMP_METHOD *meth);
从压缩抽象方法获取压缩库的NID。
const char *COMP_get_name(const COMP_METHOD *meth);
从压缩抽象方法获取压缩库的名称。
int COMP_compress_block(COMP_CTX *ctx, unsigned char *out, int olen,
unsigned char *in, int ilen);
对一段输入数据进行压缩。
成功返回压缩后的数据大小,失败返回-1。
遗憾的是,这里没有办法通过指定out为NULL,返回压缩所需的空间大小。一个可选的解决办法就是直接调用zlib的compressBound()。
int COMP_expand_block(COMP_CTX *ctx, unsigned char *out, int olen,
unsigned char *in, int ilen);
解压数据。
成功返回解压后的数据大小,失败返回-1。
使用举例:
下面这个例子演示了使用压缩API对一段数据执行压缩和解压操作。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <openssl/comp.h>
#include <zlib.h>
namespace dakuang {}
void printHex(const unsigned char* pBuf, int nLen)
{
for (int i = 0; i < nLen; ++i)
{
printf("%02x", pBuf[i]);
}
printf("\n");
}
int main(int argc, char* argv[])
{
COMP_CTX* ctx = COMP_CTX_new( COMP_zlib() );
printf("NID type:[%d] \n", COMP_CTX_get_type(ctx));
const COMP_METHOD* method = COMP_CTX_get_method(ctx);
printf("lib name:[%s] \n", COMP_get_name(method));
unsigned char sText[] = "12345678901234567890123456789012345678901234567890";
int nTextLen = strlen((char*)sText);
unsigned long nMustLen = compressBound(nTextLen);
printf("must:%d \n", nMustLen);
unsigned char* pComp = (unsigned char*)malloc(nMustLen);
int ret = COMP_compress_block(ctx, pComp, nMustLen, sText, nTextLen);
printf("compress ret:%d \n", ret);
unsigned char sExpand[128] = {0};
ret = COMP_expand_block(ctx, sExpand, 128, pComp, ret);
printf("expand ret:%d \n", ret);
printf("expand text:[%s] \n", sExpand);
free(pComp);
COMP_CTX_free(ctx);
return 0;
}
输出:
NID type:[125]
lib name:[zlib compression]
must:63
compress ret:21
expand ret:50
expand text:[12345678901234567890123456789012345678901234567890]